diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 3c9e98b4e..2b4342e0c 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -8637,6 +8637,14 @@ + + + + + + + + diff --git a/library/build.gradle b/library/build.gradle index 5512aa552..54df040b3 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -64,7 +64,7 @@ dependencies { implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' api 'com.squareup.okhttp3:okhttp:5.5.0' - implementation 'com.github.bitfireAT:dav4jvm:3.0.2' + implementation 'com.github.bitfireAT:dav4jvm:2.1.3' // in transition phase, we use old and new libs implementation group: 'com.google.code.gson', name: 'gson', version: '2.14.0' implementation 'androidx.annotation:annotation:1.10.0' diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/directediting/DirectEditingOpenFileRemoteOperationIT.java b/library/src/androidTest/java/com/nextcloud/android/lib/resources/directediting/DirectEditingOpenFileRemoteOperationIT.java index 9f23bf3a7..f4680c072 100644 --- a/library/src/androidTest/java/com/nextcloud/android/lib/resources/directediting/DirectEditingOpenFileRemoteOperationIT.java +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/directediting/DirectEditingOpenFileRemoteOperationIT.java @@ -7,6 +7,9 @@ */ package com.nextcloud.android.lib.resources.directediting; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import com.owncloud.android.AbstractIT; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; @@ -18,9 +21,6 @@ import java.io.IOException; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - public class DirectEditingOpenFileRemoteOperationIT extends AbstractIT { @Test public void openFile() throws IOException { @@ -34,7 +34,7 @@ public void openFile() throws IOException { 1464818400 ).execute(client).isSuccess()); - TestCase.assertTrue(new ReadFileRemoteOperation(remotePath).execute(client).isSuccess()); + TestCase.assertTrue(new ReadFileRemoteOperation(remotePath).execute(nextcloudClient).isSuccess()); // open file RemoteOperationResult result = new DirectEditingOpenFileRemoteOperation(remotePath, "text") @@ -58,7 +58,7 @@ public void openFileWithSpecialChars() throws IOException { 1464818400 ).execute(client).isSuccess()); - TestCase.assertTrue(new ReadFileRemoteOperation(remotePath).execute(client).isSuccess()); + TestCase.assertTrue(new ReadFileRemoteOperation(remotePath).execute(nextcloudClient).isSuccess()); // open file RemoteOperationResult result = new DirectEditingOpenFileRemoteOperation(remotePath, "text") @@ -82,7 +82,7 @@ public void openFileWithSpecialChars2() throws IOException { 1464818400 ).execute(client).isSuccess()); - TestCase.assertTrue(new ReadFileRemoteOperation(remotePath).execute(client).isSuccess()); + TestCase.assertTrue(new ReadFileRemoteOperation(remotePath).execute(nextcloudClient).isSuccess()); // open file RemoteOperationResult result = new DirectEditingOpenFileRemoteOperation(remotePath, "text") @@ -98,7 +98,7 @@ public void openFileWithSpecialChars2() throws IOException { public void openNonExistingFile() { String remotePath = "/nonExisting.md"; - TestCase.assertFalse(new ReadFileRemoteOperation(remotePath).execute(client).isSuccess()); + TestCase.assertFalse(new ReadFileRemoteOperation(remotePath).execute(nextcloudClient).isSuccess()); // open file RemoteOperationResult result = new DirectEditingOpenFileRemoteOperation(remotePath, "text") diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/files/ToggleFileLockRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/files/ToggleFileLockRemoteOperationIT.kt index ec029da67..0898f1ee3 100644 --- a/library/src/androidTest/java/com/nextcloud/android/lib/resources/files/ToggleFileLockRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/files/ToggleFileLockRemoteOperationIT.kt @@ -11,7 +11,6 @@ import com.owncloud.android.AbstractIT import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation import com.owncloud.android.lib.resources.files.model.FileLockType -import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.lib.resources.status.NextcloudVersion.Companion.nextcloud_24 import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -34,20 +33,20 @@ class ToggleFileLockRemoteOperationIT : AbstractIT() { .isSuccess ) val initialFile = - ReadFileRemoteOperation(remotePath).execute(client).singleData as RemoteFile + ReadFileRemoteOperation(remotePath).execute(nextcloudClient).resultData assertFalse("File shouldn't be locked", initialFile.isLocked) // lock file val lockResult = ToggleFileLockRemoteOperation(toLock = true, remotePath).execute(nextcloudClient) assertTrue("File lock failed", lockResult.isSuccess) - val lockFile = ReadFileRemoteOperation(remotePath).execute(client).singleData as RemoteFile + val lockFile = ReadFileRemoteOperation(remotePath).execute(nextcloudClient).resultData assertTrue("File should be locked", lockFile.isLocked) assertEquals("Wrong lock type", FileLockType.MANUAL, lockFile.lockType) // unlock again val unlockResult = ToggleFileLockRemoteOperation(toLock = false, remotePath).execute(nextcloudClient) assertTrue("File unlock failed", unlockResult.isSuccess) - val unlockFile = ReadFileRemoteOperation(remotePath).execute(client).singleData as RemoteFile + val unlockFile = ReadFileRemoteOperation(remotePath).execute(nextcloudClient).resultData assertFalse("File shouldn't be locked", unlockFile.isLocked) } } diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/search/UnifiedSearchRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/search/UnifiedSearchRemoteOperationIT.kt index 17962eed6..9fc44f763 100644 --- a/library/src/androidTest/java/com/nextcloud/android/lib/resources/search/UnifiedSearchRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/search/UnifiedSearchRemoteOperationIT.kt @@ -10,7 +10,6 @@ package com.nextcloud.android.lib.resources.search import com.owncloud.android.AbstractIT import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation -import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.lib.resources.status.NextcloudVersion import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -48,8 +47,8 @@ class UnifiedSearchRemoteOperationIT : AbstractIT() { assertTrue(CreateFolderRemoteOperation(remotePath, true).execute(client).isSuccess) val remoteFile = ReadFileRemoteOperation(remotePath) - .execute(client) - .data[0] as RemoteFile + .execute(nextcloudClient) + .resultData val fileId = remoteFile.localId val result = UnifiedSearchRemoteOperation("files", "test").execute(nextcloudClient) diff --git a/library/src/androidTest/java/com/owncloud/android/AbstractIT.java b/library/src/androidTest/java/com/owncloud/android/AbstractIT.java index 59a163689..4aa0cb1b1 100644 --- a/library/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/library/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -94,6 +94,10 @@ public static void beforeAll() throws InterruptedException, String loginName = arguments.getString("TEST_SERVER_USERNAME"); String password = arguments.getString("TEST_SERVER_PASSWORD"); + // E2E server app checks for official NC client with >=3.13.0, + // and blocks all other clients, e.g. 3rd party apps using this lib + OwnCloudClientManagerFactory.setUserAgent("Mozilla/5.0 (Android) Nextcloud-android/3.13.0"); + client = OwnCloudClientFactory.createOwnCloudClient(url, context, true); client.setCredentials(new OwnCloudBasicCredentials(loginName, password)); client.setUserId(loginName); // for test same as userId @@ -106,8 +110,6 @@ public static void beforeAll() throws InterruptedException, client2.setCredentials(new OwnCloudBasicCredentials(loginName2, password2)); client2.setUserId(loginName2); // for test same as userId - OwnCloudClientManagerFactory.setUserAgent("Mozilla/5.0 (Android) Nextcloud-android/1.0.0"); - String userId = loginName; // for test same as userId String credentials = Credentials.basic(loginName, password); nextcloudClient = new NextcloudClient(url, userId, credentials, context); diff --git a/library/src/androidTest/java/com/owncloud/android/CreateFolderRemoteOperationIT.java b/library/src/androidTest/java/com/owncloud/android/CreateFolderRemoteOperationIT.java index 4f7b02dfc..9740eea68 100644 --- a/library/src/androidTest/java/com/owncloud/android/CreateFolderRemoteOperationIT.java +++ b/library/src/androidTest/java/com/owncloud/android/CreateFolderRemoteOperationIT.java @@ -92,10 +92,10 @@ public void testFileID() { RemoteOperationResult result = new CreateFolderRemoteOperation(remotePath, true).execute(client); assertTrue(result.isSuccess()); - RemoteOperationResult readResult = new ReadFileRemoteOperation(remotePath).execute(client); + RemoteOperationResult readResult = new ReadFileRemoteOperation(remotePath).execute(nextcloudClient); assertTrue(readResult.isSuccess()); - String remoteId = ((RemoteFile) readResult.getData().get(0)).getRemoteId(); + String remoteId = readResult.getResultData().getRemoteId(); assertEquals(result.getResultData(), remoteId); } diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/comments/CommentFileRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/comments/CommentFileRemoteOperationIT.kt index f344633eb..947aa39bc 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/comments/CommentFileRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/comments/CommentFileRemoteOperationIT.kt @@ -10,7 +10,6 @@ package com.owncloud.android.lib.resources.comments import com.owncloud.android.AbstractIT import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation -import com.owncloud.android.lib.resources.files.model.RemoteFile import junit.framework.Assert.assertTrue import org.junit.Test @@ -25,8 +24,8 @@ class CommentFileRemoteOperationIT : AbstractIT() { .isSuccess ) - val readResult = ReadFileRemoteOperation(remotePath).execute(client) - val remoteFile = readResult.data.get(0) as RemoteFile + val readResult = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) + val remoteFile = readResult.resultData assertTrue( CommentFileRemoteOperation("test", remoteFile.localId) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/e2ee/UpdateMetadataRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/e2ee/UpdateMetadataRemoteOperationIT.kt index 855a52bc1..8b0f2271c 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/e2ee/UpdateMetadataRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/e2ee/UpdateMetadataRemoteOperationIT.kt @@ -14,7 +14,6 @@ import com.owncloud.android.AbstractIT import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation -import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.lib.resources.status.OwnCloudVersion import junit.framework.TestCase import org.junit.Assert @@ -33,8 +32,7 @@ class UpdateMetadataRemoteOperationIT : AbstractIT() { // create folder val folder = "/" + make(20) + "/" TestCase.assertTrue(CreateFolderRemoteOperation(folder, true).execute(client).isSuccess) - val remoteFolder = - ReadFileRemoteOperation(folder).execute(client).getSingleData() as RemoteFile? + val remoteFolder = ReadFileRemoteOperation(folder).execute(nextcloudClient).resultData TestCase.assertNotNull(remoteFolder) @@ -117,8 +115,7 @@ class UpdateMetadataRemoteOperationIT : AbstractIT() { // create folder val folder = "/" + make(20) + "/" TestCase.assertTrue(CreateFolderRemoteOperation(folder, true).execute(client).isSuccess) - val remoteFolder = - ReadFileRemoteOperation(folder).execute(client).getSingleData() as RemoteFile? + val remoteFolder = ReadFileRemoteOperation(folder).execute(nextcloudClient).resultData TestCase.assertNotNull(remoteFolder) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/CheckEtagRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/CheckEtagRemoteOperationIT.kt index 44123907c..155fbb8e0 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/CheckEtagRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/CheckEtagRemoteOperationIT.kt @@ -9,7 +9,6 @@ package com.owncloud.android.lib.resources.files import com.owncloud.android.AbstractIT import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.files.model.RemoteFile import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test @@ -26,8 +25,7 @@ class CheckEtagRemoteOperationIT : AbstractIT() { .isSuccess ) - val readResult = ReadFileRemoteOperation(remotePath).execute(client) - val remoteFile = readResult.data[0] as RemoteFile + val remoteFile = ReadFileRemoteOperation(remotePath).execute(nextcloudClient).resultData val eTag = remoteFile.etag var eTagResult = CheckEtagRemoteOperation(remotePath, eTag).execute(client) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperationIT.kt index 891c9da29..bede2b35a 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperationIT.kt @@ -8,7 +8,6 @@ package com.owncloud.android.lib.resources.files import com.owncloud.android.AbstractIT -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation import com.owncloud.android.lib.resources.files.model.GeoLocation import com.owncloud.android.lib.resources.files.model.ImageDimension @@ -23,14 +22,24 @@ import org.junit.Test class ReadFileRemoteOperationIT : AbstractIT() { @Test fun readRemoteFolder() { - val remotePath = "/test/" + val remotePath = "/folder/" assertTrue(CreateFolderRemoteOperation(remotePath, true).execute(client).isSuccess) - val result = ReadFileRemoteOperation(remotePath).execute(client) + // use ownCloud client for reference + var result = ReadFileRemoteOperation(remotePath).execute(client) + assertTrue(result.isSuccess) + val ocClientFile = result.resultData + assertEquals(remotePath, ocClientFile.remotePath) + result = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) assertTrue(result.isSuccess) - assertEquals(remotePath, (result.data[0] as RemoteFile).remotePath) + val ncClientFile = result.resultData + assertEquals(remotePath, result.resultData.remotePath) + + assertEquals(ocClientFile.modifiedTimestamp, ncClientFile.modifiedTimestamp) + + assertTrue(remoteFilesEqual(ocClientFile, ncClientFile)) } @Test @@ -68,13 +77,13 @@ class ReadFileRemoteOperationIT : AbstractIT() { ).execute(client).isSuccess ) - val movieFileResult = ReadFileRemoteOperation(movieFilePath).execute(client) + val movieFileResult = ReadFileRemoteOperation(movieFilePath).execute(nextcloudClient) assertTrue(movieFileResult.isSuccess) - val movieRemoteFile = movieFileResult.data[0] as RemoteFile + val movieRemoteFile = movieFileResult.resultData - val livePhotoResult = ReadFileRemoteOperation(livePhotoPath).execute(client) + val livePhotoResult = ReadFileRemoteOperation(livePhotoPath).execute(nextcloudClient) assertTrue(livePhotoResult.isSuccess) - val livePhotoRemoteFile = livePhotoResult.data[0] as RemoteFile + val livePhotoRemoteFile = livePhotoResult.resultData assertEquals(livePhotoRemoteFile.livePhoto, movieRemoteFile.remotePath) assertTrue(movieRemoteFile.hidden) @@ -91,10 +100,18 @@ class ReadFileRemoteOperationIT : AbstractIT() { .isSuccess ) - val result = ReadFileRemoteOperation(remotePath).execute(client) + // use ownCloud client for reference + var result = ReadFileRemoteOperation(remotePath).execute(client) + assertTrue(result.isSuccess) + val ocClientFile = result.resultData + assertEquals(remotePath, ocClientFile.remotePath) + result = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) assertTrue(result.isSuccess) - assertEquals(remotePath, (result.data[0] as RemoteFile).remotePath) + val ncClientFile = result.resultData + assertEquals(remotePath, result.resultData.remotePath) + + assertTrue(remoteFilesEqual(ocClientFile, ncClientFile)) } @Test @@ -108,10 +125,10 @@ class ReadFileRemoteOperationIT : AbstractIT() { .isSuccess ) - val result = ReadFileRemoteOperation(remotePath).execute(client) + val result = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) assertTrue(result.isSuccess) - val remoteFile = result.data[0] as RemoteFile + val remoteFile = result.resultData @Suppress("Detekt.MagicNumber") if (isServerAtLeast(NextcloudVersion.nextcloud_23)) { @@ -134,15 +151,10 @@ class ReadFileRemoteOperationIT : AbstractIT() { @Test fun readEncryptedState() { val remotePath = "/testEncryptedFolder/" - - // E2E server app checks for official NC client with >=3.13.0, - // and blocks all other clients, e.g. 3rd party apps using this lib - OwnCloudClientManagerFactory.setUserAgent("Mozilla/5.0 (Android) Nextcloud-android/3.13.0") - assertTrue(CreateFolderRemoteOperation(remotePath, true).execute(client).isSuccess) - var result = ReadFileRemoteOperation(remotePath).execute(client) - val remoteFile = result.data[0] as RemoteFile + var result = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) + val remoteFile = result.resultData assertTrue(result.isSuccess) assertFalse(remoteFile.isEncrypted) @@ -159,7 +171,47 @@ class ReadFileRemoteOperationIT : AbstractIT() { ) // re-read - result = ReadFileRemoteOperation(remotePath).execute(client) - assertEquals(true, (result.data[0] as RemoteFile).isEncrypted) + result = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) + assertEquals(true, result.resultData.isEncrypted) } + + private fun remoteFilesEqual( + a: RemoteFile, + b: RemoteFile + ): Boolean = + a.remotePath == b.remotePath && + a.mimeType == b.mimeType && + a.length == b.length && + a.creationTimestamp == b.creationTimestamp && + a.modifiedTimestamp == b.modifiedTimestamp && + a.uploadTimestamp == b.uploadTimestamp && + a.etag == b.etag && + a.permissions == b.permissions && + a.localId == b.localId && + a.remoteId == b.remoteId && + a.size == b.size && + a.isFavorite == b.isFavorite && + a.isEncrypted == b.isEncrypted && + a.mountType == b.mountType && + a.ownerId == b.ownerId && + a.ownerDisplayName == b.ownerDisplayName && + a.unreadCommentsCount == b.unreadCommentsCount && + a.isHasPreview == b.isHasPreview && + a.note == b.note && + a.sharees.contentEquals(b.sharees) && + a.richWorkspace == b.richWorkspace && + a.isLocked == b.isLocked && + a.lockType == b.lockType && + a.lockOwner == b.lockOwner && + a.lockOwnerDisplayName == b.lockOwnerDisplayName && + a.lockTimestamp == b.lockTimestamp && + a.lockOwnerEditor == b.lockOwnerEditor && + a.lockTimeout == b.lockTimeout && + a.lockToken == b.lockToken && + a.tags.contentEquals(b.tags) && + a.imageDimension == b.imageDimension && + a.geoLocation == b.geoLocation && + a.hidden == b.hidden && + a.livePhoto == b.livePhoto && + a.fileDownloadLimit == b.fileDownloadLimit } diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileVersionsRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileVersionsRemoteOperationIT.kt index 816112e11..2438e658f 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileVersionsRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/ReadFileVersionsRemoteOperationIT.kt @@ -8,7 +8,6 @@ package com.owncloud.android.lib.resources.files import com.owncloud.android.AbstractIT -import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation import com.owncloud.android.lib.resources.status.NextcloudVersion import org.junit.Assert.assertEquals @@ -33,7 +32,7 @@ class ReadFileVersionsRemoteOperationIT : AbstractIT() { assertTrue("Error uploading file $filePath: $uploadResult", uploadResult.isSuccess) - var remoteFile = ReadFileRemoteOperation(filePath).execute(client).data[0] as RemoteFile + var remoteFile = ReadFileRemoteOperation(filePath).execute(nextcloudClient).resultData var sutResult = ReadFileVersionsRemoteOperation(remoteFile.localId).execute(client) @@ -64,7 +63,7 @@ class ReadFileVersionsRemoteOperationIT : AbstractIT() { assertTrue("Error uploading file $filePath: $uploadResult", uploadResult.isSuccess) - remoteFile = ReadFileRemoteOperation(filePath).execute(client).data[0] as RemoteFile + remoteFile = ReadFileRemoteOperation(filePath).execute(nextcloudClient).resultData sutResult = ReadFileVersionsRemoteOperation(remoteFile.localId).execute(client) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationIT.java b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationIT.java index dda961ed4..94577c8e9 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationIT.java +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationIT.java @@ -57,10 +57,10 @@ public void testSearchByFileIdEmpty() { public void testSearchByFileIdSuccess() { assertTrue(new CreateFolderRemoteOperation("/test/", true).execute(client).isSuccess()); - RemoteOperationResult readFile = new ReadFileRemoteOperation("/test/").execute(client); + RemoteOperationResult readFile = new ReadFileRemoteOperation("/test/").execute(nextcloudClient); assertTrue(readFile.isSuccess()); - RemoteFile remoteFile = ((RemoteFile) readFile.getSingleData()); + RemoteFile remoteFile = readFile.getResultData(); SearchRemoteOperation sut = new SearchRemoteOperation(String.valueOf(remoteFile.getLocalId()), SearchRemoteOperation.SearchType.FILE_ID_SEARCH, false, diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperationIT.kt index 065674de3..24ffc4659 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperationIT.kt @@ -52,10 +52,10 @@ class UploadFileRemoteOperationIT : AbstractIT() { assertTrue(uploadResult.isSuccess) // ReadFileRemoteOperation - var result = ReadFileRemoteOperation(remotePath).execute(client) + val result = ReadFileRemoteOperation(remotePath).execute(nextcloudClient) assertTrue(result.isSuccess) - var remoteFile = result.data[0] as RemoteFile + var remoteFile = result.resultData assertEquals(remotePath, remoteFile.remotePath) assertEquals(creationTimestamp, remoteFile.creationTimestamp) @@ -66,10 +66,10 @@ class UploadFileRemoteOperationIT : AbstractIT() { ) // ReadFolderRemoteOperation - result = ReadFolderRemoteOperation(remotePath).execute(client) - assertTrue(result.isSuccess) + var result2 = ReadFolderRemoteOperation(remotePath).execute(client) + assertTrue(result2.isSuccess) - remoteFile = result.data[0] as RemoteFile + remoteFile = result2.data[0] as RemoteFile assertEquals(remotePath, remoteFile.remotePath) assertEquals(creationTimestamp, remoteFile.creationTimestamp) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/DeleteTagRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/DeleteTagRemoteOperationIT.kt index 673a1b665..452922c22 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/DeleteTagRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/DeleteTagRemoteOperationIT.kt @@ -29,8 +29,8 @@ class DeleteTagRemoteOperationIT : AbstractIT() { // create a folder val folder = "/deleteTagFolder/" assertTrue(CreateFolderRemoteOperation(folder, true).execute(client).isSuccess) - val folderMetadata = ReadFileRemoteOperation(folder).execute(client) - val fileId = (folderMetadata.data[0] as RemoteFile).localId + val folderMetadata = ReadFileRemoteOperation(folder).execute(nextcloudClient) + val fileId = folderMetadata.resultData.localId // create a tag val tagName = RandomStringGenerator.make(TAG_LENGTH) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/GetTagsRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/GetTagsRemoteOperationIT.kt index e518a1959..6df323fde 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/GetTagsRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/tags/GetTagsRemoteOperationIT.kt @@ -108,17 +108,17 @@ class GetTagsRemoteOperationIT : AbstractIT() { // add colored tag to file val tagFolder = "/coloredFolder/" assertTrue(CreateFolderRemoteOperation(tagFolder, true).execute(client).isSuccess) - val folderMetadata = ReadFileRemoteOperation(tagFolder).execute(client) + val folderMetadata = ReadFileRemoteOperation(tagFolder).execute(nextcloudClient) assertTrue( PutTagRemoteOperation( tag1.id, - (folderMetadata.data[0] as RemoteFile).localId + folderMetadata.resultData.localId ).execute(nextcloudClient).isSuccess ) assertTrue( PutTagRemoteOperation( tag2.id, - (folderMetadata.data[0] as RemoteFile).localId + folderMetadata.resultData.localId ).execute(nextcloudClient).isSuccess ) diff --git a/library/src/main/java/com/nextcloud/common/DavResponse.kt b/library/src/main/java/com/nextcloud/common/DavResponse.kt new file mode 100644 index 000000000..5ef3a0846 --- /dev/null +++ b/library/src/main/java/com/nextcloud/common/DavResponse.kt @@ -0,0 +1,39 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.common + +import okhttp3.Headers +import okhttp3.internal.http.StatusLine + +/** + * Encapsulates essential data returned as responses from various DAV calls. + */ +data class DavResponse( + var success: Boolean = false, + var status: StatusLine? = null, + var headers: Headers? = null +) { + /** + * Return value of specified header. + * + * Simple helper to aid with nullability when called from Java. + * + * @param key name of header to get + * @return value of header or `null` when header is not set + */ + fun getHeader(key: String): String? = headers?.get(key) + + /** + * Return value of status code. + * + * Simple helper to aid with nullability when called from Java. + * + * @return HTTP status code or `0` if not set. + */ + fun getStatusCode(): Int = status?.code ?: 0 +} diff --git a/library/src/main/java/com/nextcloud/common/NextcloudAuthenticator.kt b/library/src/main/java/com/nextcloud/common/NextcloudAuthenticator.kt new file mode 100644 index 000000000..1b1528e33 --- /dev/null +++ b/library/src/main/java/com/nextcloud/common/NextcloudAuthenticator.kt @@ -0,0 +1,52 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Your Name + * SPDX-License-Identifier: MIT + */ +package com.nextcloud.common + +import okhttp3.Authenticator +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route + +class NextcloudAuthenticator( + private val credentials: String +) : Authenticator { + @Suppress("ReturnCount") + override fun authenticate( + route: Route?, + response: Response + ): Request? { + val authenticatorType = "Authorization" + + if (response.request.header(authenticatorType) != null) { + return null + } + + var countedResponse: Response? = response + + var attemptsCount = 0 + + countedResponse = countedResponse?.priorResponse + + while (countedResponse != null) { + attemptsCount++ + if (attemptsCount == MAX_ATTEMPTS) { + return null + } + + countedResponse = countedResponse.priorResponse + } + + return response.request + .newBuilder() + .header(authenticatorType, credentials) + .build() + } + + companion object { + const val MAX_ATTEMPTS = 3 + } +} diff --git a/library/src/main/java/com/nextcloud/common/NextcloudClient.kt b/library/src/main/java/com/nextcloud/common/NextcloudClient.kt index 7f51e6437..8bf798329 100644 --- a/library/src/main/java/com/nextcloud/common/NextcloudClient.kt +++ b/library/src/main/java/com/nextcloud/common/NextcloudClient.kt @@ -82,6 +82,8 @@ class NextcloudClient private constructor( Log_OC.d(this, "Proxy settings: $proxyHost:$proxyPort") } + val userAgent = OwnCloudClientManagerFactory.getUserAgent() + return OkHttpClient .Builder() .cookieJar(CookieJar.NO_COOKIES) @@ -94,7 +96,15 @@ class NextcloudClient private constructor( .hostnameVerifier { _: String?, _: SSLSession? -> true } .fastFallback(true) .proxy(proxy) - .build() + .addNetworkInterceptor { chain -> + chain.proceed( + chain + .request() + .newBuilder() + .header("User-Agent", userAgent) + .build() + ) + }.build() } } @@ -191,6 +201,13 @@ class NextcloudClient private constructor( return result } + fun disabledRedirectClient(): OkHttpClient = + client + .newBuilder() + .followRedirects(false) + .authenticator(NextcloudAuthenticator(credentials)) + .build() + private fun setRedirectedDestinationHeader( method: OkHttpMethodBase, location: String, diff --git a/library/src/main/java/com/nextcloud/common/WebDavUtils.kt b/library/src/main/java/com/nextcloud/common/WebDavUtils.kt new file mode 100644 index 000000000..5dfceddeb --- /dev/null +++ b/library/src/main/java/com/nextcloud/common/WebDavUtils.kt @@ -0,0 +1,415 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.common + +import android.net.Uri +import at.bitfire.dav4jvm.PropertyRegistry +import at.bitfire.dav4jvm.Response +import at.bitfire.dav4jvm.property.DisplayName +import at.bitfire.dav4jvm.property.GetContentLength +import at.bitfire.dav4jvm.property.GetContentType +import at.bitfire.dav4jvm.property.GetETag +import at.bitfire.dav4jvm.property.ResourceType +import com.google.gson.Gson +import com.owncloud.android.lib.common.network.WebdavEntry +import com.owncloud.android.lib.resources.files.model.RemoteFile +import com.owncloud.android.lib.resources.files.webdav.NCCreationDate +import com.owncloud.android.lib.resources.files.webdav.NCEncrypted +import com.owncloud.android.lib.resources.files.webdav.NCFavorite +import com.owncloud.android.lib.resources.files.webdav.NCGetLastModified +import com.owncloud.android.lib.resources.files.webdav.NCHidden +import com.owncloud.android.lib.resources.files.webdav.NCLock +import com.owncloud.android.lib.resources.files.webdav.NCLockOwner +import com.owncloud.android.lib.resources.files.webdav.NCLockOwnerDisplayName +import com.owncloud.android.lib.resources.files.webdav.NCLockOwnerEditor +import com.owncloud.android.lib.resources.files.webdav.NCLockOwnerType +import com.owncloud.android.lib.resources.files.webdav.NCLockTime +import com.owncloud.android.lib.resources.files.webdav.NCLockTimeout +import com.owncloud.android.lib.resources.files.webdav.NCLockToken +import com.owncloud.android.lib.resources.files.webdav.NCMetadataGPS +import com.owncloud.android.lib.resources.files.webdav.NCMetadataLivePhoto +import com.owncloud.android.lib.resources.files.webdav.NCMetadataPhotosGPS +import com.owncloud.android.lib.resources.files.webdav.NCMetadataPhotosSize +import com.owncloud.android.lib.resources.files.webdav.NCMetadataSize +import com.owncloud.android.lib.resources.files.webdav.NCMountType +import com.owncloud.android.lib.resources.files.webdav.NCNote +import com.owncloud.android.lib.resources.files.webdav.NCPermissions +import com.owncloud.android.lib.resources.files.webdav.NCPreview +import com.owncloud.android.lib.resources.files.webdav.NCRichWorkspace +import com.owncloud.android.lib.resources.files.webdav.NCSharees +import com.owncloud.android.lib.resources.files.webdav.NCTags +import com.owncloud.android.lib.resources.files.webdav.NCTrashbinDeletionTime +import com.owncloud.android.lib.resources.files.webdav.NCTrashbinFilename +import com.owncloud.android.lib.resources.files.webdav.NCTrashbinLocation +import com.owncloud.android.lib.resources.files.webdav.NCUploadTime +import com.owncloud.android.lib.resources.files.webdav.OCCommentsUnread +import com.owncloud.android.lib.resources.files.webdav.OCDisplayName +import com.owncloud.android.lib.resources.files.webdav.OCId +import com.owncloud.android.lib.resources.files.webdav.OCLocalId +import com.owncloud.android.lib.resources.files.webdav.OCOwnerDisplayName +import com.owncloud.android.lib.resources.files.webdav.OCOwnerId +import com.owncloud.android.lib.resources.files.webdav.OCSize +import java.net.URLDecoder +import java.text.SimpleDateFormat +import java.util.Locale + +object WebDavUtils { + const val NAMESPACE_OC = "http://owncloud.org/ns" + const val NAMESPACE_NC = "http://nextcloud.org/ns" + + internal val gson = Gson() + + private val DATETIME_FORMATS = + arrayOf( + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US), + SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US), + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US), + SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US), + SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), + SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US), + SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.US) + ) + + object PROPERTYSETS { + val ALL = + arrayOf( + DisplayName.NAME, + GetContentType.NAME, + ResourceType.NAME, + GetContentLength.NAME, + NCGetLastModified.NAME, + NCCreationDate.NAME, + GetETag.NAME, + NCPermissions.NAME, + OCLocalId.NAME, + OCId.NAME, + OCSize.NAME, + NCFavorite.NAME, + NCEncrypted.NAME, + NCMountType.NAME, + OCOwnerId.NAME, + OCOwnerDisplayName.NAME, + OCCommentsUnread.NAME, + NCPreview.NAME, + NCNote.NAME, + NCSharees.NAME, + NCRichWorkspace.NAME, + NCCreationDate.NAME, + NCUploadTime.NAME, + NCLock.NAME, + NCLockOwnerType.NAME, + NCLockOwner.NAME, + NCLockOwnerDisplayName.NAME, + NCLockOwnerEditor.NAME, + NCLockTime.NAME, + NCLockTimeout.NAME, + NCLockToken.NAME, + NCTags.NAME, + NCMetadataSize.NAME, + NCMetadataGPS.NAME, + NCMetadataSize.NAME, + NCMetadataPhotosSize.NAME, + NCMetadataPhotosGPS.NAME, + NCMetadataLivePhoto.NAME, + NCHidden.NAME + ) + + val FILE = + arrayOf( + DisplayName.NAME, + GetContentType.NAME, + ResourceType.NAME, + GetContentLength.NAME, + NCGetLastModified.NAME, + NCCreationDate.NAME, + GetETag.NAME, + NCPermissions.NAME, + OCLocalId.NAME, + OCId.NAME, + OCSize.NAME, + NCFavorite.NAME, + NCPreview.NAME, + NCSharees.NAME, + NCUploadTime.NAME, + NCLock.NAME, + NCLockOwnerType.NAME, + NCLockOwner.NAME, + NCLockOwnerDisplayName.NAME, + NCLockOwnerEditor.NAME, + NCLockTime.NAME, + NCLockTimeout.NAME, + NCLockToken.NAME, + NCEncrypted.NAME, + NCTags.NAME, + NCMetadataSize.NAME, + NCMetadataGPS.NAME, + NCMetadataPhotosSize.NAME, + NCMetadataPhotosGPS.NAME, + NCMetadataLivePhoto.NAME, + NCHidden.NAME + ) + + val TRASHBIN = + arrayOf( + ResourceType.NAME, + GetContentType.NAME, + GetContentLength.NAME, + OCSize.NAME, + OCId.NAME, + NCTrashbinFilename.NAME, + NCTrashbinLocation.NAME, + NCTrashbinDeletionTime.NAME + ) + + val FILE_VERSION = + arrayOf( + GetContentType.NAME, + ResourceType.NAME, + GetContentLength.NAME, + NCGetLastModified.NAME, + NCCreationDate.NAME, + OCId.NAME, + OCSize.NAME + ) + + val CHUNK = + arrayOf( + GetContentType.NAME, + ResourceType.NAME, + GetContentLength.NAME + ) + } + + fun registerCustomFactories() { + val list = + listOf( + NCCreationDate.Factory(), + NCEncrypted.Factory(), + GetETag.Factory(), + NCFavorite.Factory(), + NCGetLastModified.Factory(), + NCHidden.Factory(), + NCLock.Factory(), + NCLockOwnerDisplayName.Factory(), + NCLockOwnerEditor.Factory(), + NCLockOwner.Factory(), + NCLockOwnerType.Factory(), + NCLockTime.Factory(), + NCLockTimeout.Factory(), + NCLockToken.Factory(), + NCMetadataGPS.Factory(), + NCMetadataLivePhoto.Factory(), + NCMetadataPhotosGPS.Factory(), + NCMetadataPhotosSize.Factory(), + NCMetadataSize.Factory(), + NCMountType.Factory(), + NCNote.Factory(), + NCPermissions.Factory(), + NCPreview.Factory(), + NCRichWorkspace.Factory(), + NCSharees.Factory(), + NCTags.Factory(), + NCTrashbinDeletionTime.Factory(), + NCTrashbinFilename.Factory(), + NCTrashbinLocation.Factory(), + NCUploadTime.Factory(), + OCCommentsUnread.Factory(), + OCDisplayName.Factory(), + OCId.Factory(), + OCLocalId.Factory(), + OCOwnerDisplayName.Factory(), + OCOwnerId.Factory(), + OCSize.Factory() + ) + PropertyRegistry.register(list) + } + + @Suppress("LongMethod") + fun parseResponse( + response: Response, + filesDavUri: Uri + ): RemoteFile { + val remoteFile = RemoteFile() + + val path = "/" + URLDecoder.decode(response.href.toString().substringAfter(filesDavUri.toString()), "UTF-8") + + for (property in response.properties) { + when (property) { + is DisplayName -> { + remoteFile.name = property.displayName ?: "" + } + + is GetContentLength -> { + remoteFile.length = property.contentLength + } + + is GetContentType -> { + remoteFile.mimeType = (property.type ?: "").toString() + } + + is ResourceType -> { + if (property.types.contains(ResourceType.COLLECTION)) { + remoteFile.mimeType = WebdavEntry.DIR_TYPE + } + } + + is NCCreationDate -> { + remoteFile.creationTimestamp = property.creationDate + } + + is NCEncrypted -> { + remoteFile.isEncrypted = property.encrypted + } + + is GetETag -> { + remoteFile.etag = property.eTag + } + + is NCFavorite -> { + remoteFile.isFavorite = property.favorite + } + + is NCGetLastModified -> { + remoteFile.modifiedTimestamp = property.lastModified + } + + is NCHidden -> { + remoteFile.hidden = property.hidden + } + + is NCLock -> { + remoteFile.isLocked = property.locked + } + + is NCLockOwner -> { + remoteFile.lockOwner = property.lockOwner + } + + is NCLockOwnerDisplayName -> { + remoteFile.lockOwnerDisplayName = property.lockOwnerDisplayName + } + + is NCLockOwnerEditor -> { + remoteFile.lockOwnerEditor = property.lockOwnerEditor + } + + is NCLockOwnerType -> { + remoteFile.lockType = property.lockOwnerType + } + + is NCLockTime -> { + remoteFile.lockTimestamp = property.lockTime + } + + is NCLockTimeout -> { + remoteFile.lockTimeout = property.lockTimeout + } + + is NCLockToken -> { + remoteFile.lockToken = property.lockToken + } + + is NCMetadataGPS -> { + remoteFile.geoLocation = property.geoLocation + } + + is NCMetadataLivePhoto -> { + remoteFile.livePhoto = property.livePhoto + } + + is NCMetadataPhotosGPS -> { + remoteFile.geoLocation = property.geoLocation + } + + is NCMetadataPhotosSize -> { + remoteFile.imageDimension = property.imageDimension + } + + is NCMetadataSize -> { + remoteFile.imageDimension = property.imageDimension + } + + is NCMountType -> { + remoteFile.mountType = property.mountType + } + + is NCNote -> { + remoteFile.note = property.note + } + + is NCPermissions -> { + remoteFile.permissions = property.permissions + } + + is NCPreview -> { + remoteFile.isHasPreview = property.preview + } + + is NCRichWorkspace -> { + remoteFile.richWorkspace = property.richWorkspace + } + + is NCSharees -> { + remoteFile.sharees = property.sharees + } + + is NCTags -> { + remoteFile.tags = property.tags + } + + is NCTrashbinDeletionTime -> { /* TODO */ } + + is NCTrashbinFilename -> { /* TODO */ } + + is NCTrashbinLocation -> { /* TODO */ } + + is NCUploadTime -> { + remoteFile.uploadTimestamp = property.uploadTime + } + + is OCCommentsUnread -> { + remoteFile.unreadCommentsCount = property.commentsCount + } + + is OCDisplayName -> { + remoteFile.name = property.displayName + } + + is OCId -> { + remoteFile.remoteId = property.id + } + + is OCLocalId -> { + remoteFile.localId = property.localId + } + + is OCOwnerDisplayName -> { + remoteFile.ownerDisplayName = property.ownerDisplayName ?: "" + } + + is OCOwnerId -> { + remoteFile.ownerId = property.ownerId ?: "" + } + + is OCSize -> { + remoteFile.size = property.size + } + } + } + + remoteFile.remotePath = path + + // displayName not set - get from path + if (remoteFile.name?.isEmpty() == true) { + remoteFile.name = path.substringAfterLast("/") + } + + return remoteFile + } +} diff --git a/library/src/main/java/com/nextcloud/operations/PropFindMethod2.kt b/library/src/main/java/com/nextcloud/operations/PropFindMethod2.kt new file mode 100644 index 000000000..0cacee709 --- /dev/null +++ b/library/src/main/java/com/nextcloud/operations/PropFindMethod2.kt @@ -0,0 +1,10 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.operations + +class PropFindMethod2 diff --git a/library/src/main/java/com/nextcloud/operations/PropFindResult.kt b/library/src/main/java/com/nextcloud/operations/PropFindResult.kt new file mode 100644 index 000000000..0dcfe646f --- /dev/null +++ b/library/src/main/java/com/nextcloud/operations/PropFindResult.kt @@ -0,0 +1,19 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.operations + +import com.nextcloud.common.DavResponse +import com.owncloud.android.lib.resources.files.model.RemoteFile + +data class PropFindResult( + val davResponse: DavResponse = DavResponse(), + var root: RemoteFile = RemoteFile(), + val children: MutableList = mutableListOf() +) { + fun getContent(): List = listOf(root) + children +} diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/ExtendedProperties.kt b/library/src/main/java/com/owncloud/android/lib/common/network/ExtendedProperties.kt new file mode 100644 index 000000000..ce48ddf77 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/common/network/ExtendedProperties.kt @@ -0,0 +1,59 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Your Name + * SPDX-License-Identifier: MIT + */ +package com.owncloud.android.lib.common.network + +import at.bitfire.dav4jvm.Property + +enum class ExtendedProperties( + val value: String, + val namespace: String +) { + COMMENTS_READ_MARKER("readMarker", WebdavUtils.NAMESPACE_NC), + DISPLAY_NAME("display-name", WebdavUtils.NAMESPACE_OC), + FAVORITE("favorite", WebdavUtils.NAMESPACE_OC), + HAS_PREVIEW("has-preview", WebdavUtils.NAMESPACE_NC), + HIDDEN("hidden", WebdavUtils.NAMESPACE_NC), + IS_ENCRYPTED("is-encrypted", WebdavUtils.NAMESPACE_NC), + LOCK("lock", WebdavUtils.NAMESPACE_NC), + LOCK_OWNER("lock-owner", WebdavUtils.NAMESPACE_NC), + LOCK_OWNER_DISPLAY_NAME("lock-owner-displayname", WebdavUtils.NAMESPACE_NC), + LOCK_OWNER_EDITOR("lock-owner-editor", WebdavUtils.NAMESPACE_NC), + LOCK_OWNER_TYPE("lock-owner-type", WebdavUtils.NAMESPACE_NC), + LOCK_TIME("lock-time", WebdavUtils.NAMESPACE_NC), + LOCK_TIMEOUT("lock-timeout", WebdavUtils.NAMESPACE_NC), + LOCK_TOKEN("lock-token", WebdavUtils.NAMESPACE_NC), + + @Deprecated("Removed with v28. Use METADATA_PHOTOS_GPS instead.") + METADATA_GPS("file-metadata-gps", WebdavUtils.NAMESPACE_NC), + METADATA_LIVE_PHOTO("metadata-files-live-photo", WebdavUtils.NAMESPACE_NC), + METADATA_PHOTOS_GPS("metadata-photos-gps", WebdavUtils.NAMESPACE_NC), + METADATA_PHOTOS_SIZE("metadata-photos-size", WebdavUtils.NAMESPACE_NC), + + @Deprecated("Removed with v28. Use METADATA_PHOTOS_SIZE instead.") + METADATA_SIZE("file-metadata-size", WebdavUtils.NAMESPACE_NC), + MOUNT_TYPE("mount-type", WebdavUtils.NAMESPACE_NC), + NAME_LOCAL_ID("fileid", WebdavUtils.NAMESPACE_OC), + NAME_PERMISSIONS("permissions", WebdavUtils.NAMESPACE_OC), + NAME_REMOTE_ID("id", WebdavUtils.NAMESPACE_OC), + NAME_SIZE("size", WebdavUtils.NAMESPACE_OC), + NOTE("note", WebdavUtils.NAMESPACE_NC), + OWNER_DISPLAY_NAME("owner-display-name", WebdavUtils.NAMESPACE_OC), + OWNER_ID("owner-id", WebdavUtils.NAMESPACE_OC), + RICH_WORKSPACE("rich-workspace", WebdavUtils.NAMESPACE_NC), + SHAREES("sharees", WebdavUtils.NAMESPACE_NC), + SHAREES_DISPLAY_NAME("display-name", WebdavUtils.NAMESPACE_NC), + SHAREES_ID("id", WebdavUtils.NAMESPACE_NC), + SHAREES_SHARE_TYPE("type", WebdavUtils.NAMESPACE_NC), + SYSTEM_TAGS("system-tags", WebdavUtils.NAMESPACE_NC), + TRASHBIN_DELETION_TIME("trashbin-deletion-time", WebdavUtils.NAMESPACE_NC), + TRASHBIN_FILENAME("trashbin-filename", WebdavUtils.NAMESPACE_NC), + TRASHBIN_ORIGINAL_LOCATION("trashbin-original-location", WebdavUtils.NAMESPACE_NC), + UNREAD_COMMENTS("comments-unread", WebdavUtils.NAMESPACE_OC), + UPLOAD_TIME("upload_time", WebdavUtils.NAMESPACE_NC); + + fun toPropertyName(): Property.Name = Property.Name(namespace, value) +} diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt index 45178b9ed..eece446f1 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt @@ -153,7 +153,7 @@ class WebdavEntry constructor( if (prop != null) { val value = prop.value if (value != null) { - contentType = "DIR" // a specific attribute would be better, + contentType = DIR_TYPE // a specific attribute would be better, // but this is enough; // unless while we have no reason to distinguish // MIME types for folders @@ -613,7 +613,7 @@ class WebdavEntry constructor( fun decodedPath(): String = Uri.decode(path) val isDirectory: Boolean - get() = "DIR" == contentType + get() = DIR_TYPE == contentType private fun resetData() { permissions = null @@ -690,6 +690,7 @@ class WebdavEntry constructor( const val PROPERTY_DATE_RANGE = "dateRange" const val PROPERTY_COLLABORATORS = "collaborators" const val COLLABORATORS_SHARE_LABEL = "label" + const val DIR_TYPE = "DIR" private const val IS_ENCRYPTED = "1" private const val CODE_PROP_NOT_FOUND = 404 } diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java index 338d7fafd..47a250ddd 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java @@ -16,6 +16,8 @@ import android.net.Uri; +import androidx.annotation.Nullable; + import com.nextcloud.common.OkHttpMethodBase; import org.apache.commons.httpclient.Header; @@ -29,11 +31,13 @@ import java.util.Date; import java.util.Locale; -import androidx.annotation.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @SuppressFBWarnings("FS") public class WebdavUtils { + public static String NAMESPACE_OC = "http://owncloud.org/ns"; + public static String NAMESPACE_NC = "http://nextcloud.org/ns"; + private static final SimpleDateFormat DATETIME_FORMATS[] = { new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US), new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperation.java deleted file mode 100644 index 52a97a839..000000000 --- a/library/src/main/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperation.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Nextcloud Android Library - * - * SPDX-FileCopyrightText: 2015 ownCloud Inc. - * SPDX-License-Identifier: MIT - */ -package com.owncloud.android.lib.resources.files; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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.utils.Log_OC; -import com.owncloud.android.lib.resources.files.model.RemoteFile; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.DavConstants; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; - -import java.util.ArrayList; - - -/** - * Remote operation performing the read a file from the ownCloud server. - * - * @author David A. Velasco - * @author masensio - */ - -public class ReadFileRemoteOperation extends RemoteOperation { - - private static final String TAG = ReadFileRemoteOperation.class.getSimpleName(); - private final String mRemotePath; - private final SessionTimeOut sessionTimeOut; - - - /** - * Constructor - * - * @param remotePath Remote path of the file. - */ - public ReadFileRemoteOperation(String remotePath) { - this(remotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public ReadFileRemoteOperation(String remotePath, SessionTimeOut sessionTimeOut) { - mRemotePath = remotePath; - this.sessionTimeOut = sessionTimeOut; - } - - /** - * Performs the read operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - PropFindMethod propfind = null; - RemoteOperationResult result = null; - - /// take the duty of check the server for the current state of the file there - try { - // remote request - propfind = new PropFindMethod(client.getFilesDavUri(mRemotePath), - WebdavUtils.getFilePropSet(), // PropFind Properties - DavConstants.DEPTH_0); - int status; - status = client.executeMethod(propfind, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - - boolean isSuccess = ( - status == HttpStatus.SC_MULTI_STATUS || - status == HttpStatus.SC_OK - ); - if (isSuccess) { - // Parse response - MultiStatus resp = propfind.getResponseBodyAsMultiStatus(); - WebdavEntry we = new WebdavEntry(resp.getResponses()[0], - client.getFilesDavUri().getEncodedPath()); - RemoteFile remoteFile = new RemoteFile(we); - ArrayList files = new ArrayList(); - files.add(remoteFile); - - // Result of the operation - result = new RemoteOperationResult<>(true, propfind); - result.setData(files); - - } else { - result = new RemoteOperationResult<>(false, propfind); - client.exhaustResponse(propfind.getResponseBodyAsStream()); - } - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Log_OC.e(TAG, "Read file " + mRemotePath + " failed: " + result.getLogMessage(), - result.getException()); - } finally { - if (propfind != null) - propfind.releaseConnection(); - } - return result; - } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperation.kt new file mode 100644 index 000000000..c731f5ed0 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/ReadFileRemoteOperation.kt @@ -0,0 +1,125 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2015 ownCloud Inc. + * SPDX-License-Identifier: MIT + */ +package com.owncloud.android.lib.resources.files + +import androidx.core.net.toUri +import at.bitfire.dav4jvm.DavCollection +import com.nextcloud.common.NextcloudClient +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.WebDavUtils +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.utils.Log_OC +import com.owncloud.android.lib.resources.files.model.RemoteFile +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.DavConstants +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod + +/** + * Remote operation performing the read a file from the ownCloud server. + * + * @author David A. Velasco + * @author masensio + */ +class ReadFileRemoteOperation + @JvmOverloads + constructor( + private val mRemotePath: String?, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + @Suppress("Detekt.TooGenericExceptionCaught") // legacy code + override fun run(client: OwnCloudClient): RemoteOperationResult { + var propfind: PropFindMethod? = null + var result: RemoteOperationResult + + try { + // remote request + propfind = + PropFindMethod( + client.getFilesDavUri(mRemotePath), + WebdavUtils.getFilePropSet(), // PropFind Properties + DavConstants.DEPTH_0 + ) + val status: Int + status = + client.executeMethod( + propfind, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + + val isSuccess = ( + status == HttpStatus.SC_MULTI_STATUS || + status == HttpStatus.SC_OK + ) + if (isSuccess) { + // Parse response + val resp = propfind.getResponseBodyAsMultiStatus() + val we = + WebdavEntry( + resp.getResponses()[0], + client.getFilesDavUri().getEncodedPath()!! + ) + // Result of the operation + result = RemoteOperationResult(true, propfind) + result.resultData = RemoteFile(we) + } else { + result = RemoteOperationResult(false, propfind) + client.exhaustResponse(propfind.getResponseBodyAsStream()) + } + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e( + TAG, + "Read file " + mRemotePath + " failed: " + result.getLogMessage(), + result.getException() + ) + } finally { + if (propfind != null) { + propfind.releaseConnection() + } + } + return result + } + + @Suppress("SpreadOperator", "Detekt.TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult { + WebDavUtils.registerCustomFactories() + + var result: RemoteFile? = null + val location = client.getFilesDavUri(mRemotePath!!).toHttpUrl() + + val davCollection = DavCollection(client.disabledRedirectClient(), location) + + try { + davCollection.propfind(depth = 1, *WebDavUtils.PROPERTYSETS.ALL) { response, _ -> + if (response.isSuccess()) { + result = WebDavUtils.parseResponse(response, client.getFilesDavUri("/").toUri()) + } + } + + return if (result == null) { + RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR) + } else { + RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + resultData = result + } + } + } catch (e: Exception) { + return RemoteOperationResult(e) + } + } + + companion object { + private val TAG: String = ReadFileRemoteOperation::class.java.getSimpleName() + } + } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt index 4c9c56761..cb3f2c9d4 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/model/RemoteFile.kt @@ -22,7 +22,7 @@ import java.io.Serializable * * @author masensio */ -class RemoteFile : +class RemoteFile() : Parcelable, Serializable { var remotePath: String? = null @@ -43,7 +43,8 @@ class RemoteFile : var ownerDisplayName: String? = null var unreadCommentsCount = 0 var isHasPreview = false - var note: String? = null + var note: String? = "" + var name: String? = null var sharees: Array? = null var richWorkspace: String? = null var isLocked = false @@ -54,17 +55,13 @@ class RemoteFile : var lockOwnerEditor: String? = null var lockTimeout: Long = 0 var lockToken: String? = null - var tags: Array? = null + var tags: Array = emptyArray() var imageDimension: ImageDimension? = null var geoLocation: GeoLocation? = null var hidden = false var livePhoto: String? = null var fileDownloadLimit: List = emptyList() - constructor() { - resetData() - } - /** * Create new [RemoteFile] with given path. * @@ -73,8 +70,7 @@ class RemoteFile : * * @param path The remote path of the file. */ - constructor(path: String?) { - resetData() + constructor(path: String?) : this() { require(!(path.isNullOrEmpty() || !path.startsWith(FileUtils.PATH_SEPARATOR))) { "Trying to create a OCFile with a non valid remote path: $path" } @@ -145,7 +141,7 @@ class RemoteFile : lockTimestamp = 0 lockTimeout = 0 lockToken = null - tags = null + tags = emptyArray() hidden = false livePhoto = null fileDownloadLimit = emptyList() @@ -156,7 +152,7 @@ class RemoteFile : * * @param source The source parcel */ - private constructor(source: Parcel) { + private constructor(source: Parcel) : this() { readFromParcel(source) } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCCreationDate.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCCreationDate.kt new file mode 100644 index 000000000..bd1a91487 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCCreationDate.kt @@ -0,0 +1,39 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.NS_WEBDAV +import com.owncloud.android.lib.common.network.WebdavUtils +import org.xmlpull.v1.XmlPullParser + +class NCCreationDate private constructor( + val creationDate: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCCreationDate { + XmlUtils.readText(parser)?.let { rawDate -> + val date = WebdavUtils.parseResponseDate(rawDate) + if (date != null) { + return NCCreationDate(date.time / SECOND_IN_MILLIS) + } + } + return NCCreationDate(0) + } + } + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV, "creationdate") + const val SECOND_IN_MILLIS = 1000 + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCEncrypted.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCEncrypted.kt new file mode 100644 index 000000000..0e87bb2f1 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCEncrypted.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCEncrypted private constructor( + val encrypted: Boolean +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCEncrypted("1" == text) + } + } catch (e: IOException) { + Log.e("NCEncrypted", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCEncrypted", "failed to create property", e) + } + return NCEncrypted(false) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.IS_ENCRYPTED.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCEtag.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCEtag.kt new file mode 100644 index 000000000..d2df8119d --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCEtag.kt @@ -0,0 +1,36 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.NS_WEBDAV +import com.owncloud.android.lib.common.network.WebdavUtils +import org.xmlpull.v1.XmlPullParser + +class NCEtag private constructor( + val etag: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCEtag { + // + XmlUtils.readText(parser)?.let { rawEtag -> + return NCEtag(WebdavUtils.parseEtag(rawEtag)) + } + return NCEtag(null) + } + } + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV, "getetag") + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCFavorite.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCFavorite.kt new file mode 100644 index 000000000..11e4e7e0b --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCFavorite.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCFavorite private constructor( + val favorite: Boolean +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCFavorite("1" == text) + } + } catch (e: IOException) { + Log.e("OCFavorite", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCFavorite", "failed to create property", e) + } + return NCFavorite(false) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.FAVORITE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCGetLastModified.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCGetLastModified.kt new file mode 100644 index 000000000..ef5eb2bf9 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCGetLastModified.kt @@ -0,0 +1,39 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import at.bitfire.dav4jvm.XmlUtils.NS_WEBDAV +import com.owncloud.android.lib.common.network.WebdavUtils +import org.xmlpull.v1.XmlPullParser + +class NCGetLastModified private constructor( + val lastModified: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCGetLastModified { + // + XmlUtils.readText(parser)?.let { rawDate -> + val date = WebdavUtils.parseResponseDate(rawDate) + if (date != null) { + return NCGetLastModified(date.time) + } + } + return NCGetLastModified(0) + } + } + + companion object { + @JvmField + val NAME = Property.Name(NS_WEBDAV, "getlastmodified") + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCHidden.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCHidden.kt new file mode 100644 index 000000000..5b40ea92a --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCHidden.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCHidden private constructor( + val hidden: Boolean +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCHidden("true" == text) + } + } catch (e: IOException) { + Log.e("NCEncrypted", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCEncrypted", "failed to create property", e) + } + return NCHidden(false) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.HIDDEN.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLock.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLock.kt new file mode 100644 index 000000000..48d479ca6 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLock.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLock private constructor( + val locked: Boolean +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCLock("1" == text) + } + } catch (e: IOException) { + Log.e("NCEncrypted", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCEncrypted", "failed to create property", e) + } + return NCLock(false) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwner.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwner.kt new file mode 100644 index 000000000..cb7431661 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwner.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLockOwner private constructor( + val lockOwner: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCLockOwner(text) + } + } catch (e: IOException) { + Log.e("NCLockOwner", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCLockOwner", "failed to create property", e) + } + return NCLockOwner(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_OWNER.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerDisplayName.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerDisplayName.kt new file mode 100644 index 000000000..42dd6039a --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerDisplayName.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLockOwnerDisplayName private constructor( + val lockOwnerDisplayName: String +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCLockOwnerDisplayName(text) + } + } catch (e: IOException) { + Log.e("NCOwnerDisplayName", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCOwnerDisplayName", "failed to create property", e) + } + return NCLockOwnerDisplayName("") + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_OWNER_DISPLAY_NAME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerEditor.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerEditor.kt new file mode 100644 index 000000000..1ad50f1ba --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerEditor.kt @@ -0,0 +1,45 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLockOwnerEditor private constructor( + val lockOwnerEditor: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCLockOwnerEditor(text) + } + } catch (e: IOException) { + Log.e("NCLockOwnerEditor", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCLockOwnerEditor", "failed to create property", e) + } + return NCLockOwnerEditor(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_OWNER_EDITOR.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerType.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerType.kt new file mode 100644 index 000000000..cc2884a24 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockOwnerType.kt @@ -0,0 +1,45 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.files.model.FileLockType +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLockOwnerType private constructor( + val lockOwnerType: FileLockType? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCLockOwnerType(FileLockType.fromValue(text.toInt())) + } + } catch (e: IOException) { + Log.e("NCLockOwnerType", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCLockOwnerType", "failed to create property", e) + } + return NCLockOwnerType(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_OWNER_TYPE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockTime.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockTime.kt new file mode 100644 index 000000000..009c98f66 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockTime.kt @@ -0,0 +1,34 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser + +class NCLockTime private constructor( + val lockTime: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + XmlUtils.readText(parser)?.let { date -> + return NCLockTime(date.toLong()) + } + return NCLockTime(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_TIME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockTimeout.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockTimeout.kt new file mode 100644 index 000000000..473f926ae --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockTimeout.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLockTimeout private constructor( + val lockTimeout: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCLockTimeout(text.toLong()) + } + } catch (e: IOException) { + Log.e("NCLockTimeout", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCLockTimeout", "failed to create property", e) + } + return NCLockTimeout(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_TIMEOUT.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockToken.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockToken.kt new file mode 100644 index 000000000..d14aca518 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCLockToken.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCLockToken private constructor( + val lockToken: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCLockToken(text) + } + } catch (e: IOException) { + Log.e("NCLockToken", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCLockToken", "failed to create property", e) + } + return NCLockToken(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.LOCK_TOKEN.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataGPS.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataGPS.kt new file mode 100644 index 000000000..0f8c5d652 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataGPS.kt @@ -0,0 +1,47 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.google.gson.Gson +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.files.model.GeoLocation +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCMetadataGPS private constructor( + val geoLocation: GeoLocation? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + val geoLocation = Gson().fromJson(text, GeoLocation::class.java) + return NCMetadataGPS(geoLocation) + } + } catch (e: IOException) { + Log.e("NCMetadataGPS", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCMetadataGPS", "failed to create property", e) + } + return NCMetadataGPS(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.METADATA_GPS.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataLivePhoto.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataLivePhoto.kt new file mode 100644 index 000000000..dede31284 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataLivePhoto.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCMetadataLivePhoto private constructor( + val livePhoto: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCMetadataLivePhoto(text) + } + } catch (e: IOException) { + Log.e("NCOwnerDisplayName", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCOwnerDisplayName", "failed to create property", e) + } + return NCMetadataLivePhoto(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.METADATA_LIVE_PHOTO.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataPhotosGPS.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataPhotosGPS.kt new file mode 100644 index 000000000..5a4dcfc3b --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataPhotosGPS.kt @@ -0,0 +1,62 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.files.model.GeoLocation +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCMetadataPhotosGPS private constructor( + val geoLocation: GeoLocation +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property = NCMetadataPhotosGPS(parseText(parser)) + + @Suppress("NestedBlockDepth") + private fun parseText(parser: XmlPullParser): GeoLocation { + var latitude = 0.0 + var longitude = 0.0 + + val depth = parser.depth + var eventType = parser.eventType + + try { + while (eventType != XmlPullParser.END_TAG || parser.depth != depth) { + if (eventType != XmlPullParser.TEXT) { + when (parser.propertyName().name) { + "latitude" -> readText(parser)?.let { latitude = it.toDouble() } + "longitude" -> readText(parser)?.let { longitude = it.toDouble() } + } + } + + eventType = parser.next() + } + } catch (e: IOException) { + Log.e("NCMetadataPhotosGPS", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCMetadataPhotosGPS", "failed to create property", e) + } + + return GeoLocation(latitude, longitude) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.METADATA_PHOTOS_GPS.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataPhotosSize.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataPhotosSize.kt new file mode 100644 index 000000000..64d7ad699 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataPhotosSize.kt @@ -0,0 +1,62 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.files.model.ImageDimension +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCMetadataPhotosSize private constructor( + val imageDimension: ImageDimension +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property = NCMetadataPhotosSize(parseText(parser)) + + @Suppress("NestedBlockDepth") + private fun parseText(parser: XmlPullParser): ImageDimension { + var width = 0.0f + var height = 0.0f + + val depth = parser.depth + var eventType = parser.eventType + + try { + while (eventType != XmlPullParser.END_TAG || parser.depth != depth) { + if (eventType != XmlPullParser.TEXT) { + when (parser.propertyName().name) { + "width" -> readText(parser)?.let { width = it.toFloat() } + "height" -> readText(parser)?.let { height = it.toFloat() } + } + } + + eventType = parser.next() + } + } catch (e: IOException) { + Log.e("NCMetadataPhotosSize", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCMetadataPhotosSize", "failed to create property", e) + } + + return ImageDimension(width, height) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.METADATA_PHOTOS_SIZE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataSize.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataSize.kt new file mode 100644 index 000000000..d691ba32b --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMetadataSize.kt @@ -0,0 +1,47 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.google.gson.Gson +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.files.model.ImageDimension +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCMetadataSize private constructor( + val imageDimension: ImageDimension? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + val imageDimension = Gson().fromJson(text, ImageDimension::class.java) + return NCMetadataSize(imageDimension) + } + } catch (e: IOException) { + Log.e("NCMetadataSize", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCMetadataSize", "failed to create property", e) + } + return NCMetadataSize(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.METADATA_SIZE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMountType.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMountType.kt new file mode 100644 index 000000000..3fdcc6bd7 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCMountType.kt @@ -0,0 +1,40 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.common.network.WebdavEntry +import org.xmlpull.v1.XmlPullParser + +class NCMountType private constructor( + val mountType: WebdavEntry.MountType +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCMountType { + // (#PCDATA) > + val type = XmlUtils.readText(parser) + return NCMountType( + when (type) { + "external" -> WebdavEntry.MountType.EXTERNAL + "group" -> WebdavEntry.MountType.GROUP + else -> WebdavEntry.MountType.INTERNAL + } + ) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.MOUNT_TYPE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCNote.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCNote.kt new file mode 100644 index 000000000..f4cc01e8a --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCNote.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCNote private constructor( + val note: String +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCNote(text) + } + } catch (e: IOException) { + Log.e("NCNote", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCNote", "failed to create property", e) + } + return NCNote("") + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.NOTE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCPermissions.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCPermissions.kt new file mode 100644 index 000000000..f7b61d030 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCPermissions.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCPermissions private constructor( + val permissions: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCPermissions(text) + } + } catch (e: IOException) { + Log.e("NCPermissions", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCPermissions", "failed to create property", e) + } + return NCPermissions(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.NAME_PERMISSIONS.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCPreview.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCPreview.kt new file mode 100644 index 000000000..76cf95ad2 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCPreview.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCPreview private constructor( + val preview: Boolean +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCPreview(text.toBoolean()) + } + } catch (e: IOException) { + Log.e("NCPreview", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCPreview", "failed to create property", e) + } + return NCPreview(false) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.HAS_PREVIEW.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCRichWorkspace.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCRichWorkspace.kt new file mode 100644 index 000000000..545ba497c --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCRichWorkspace.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCRichWorkspace private constructor( + val richWorkspace: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCRichWorkspace { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCRichWorkspace(text) + } + } catch (e: IOException) { + Log.e("NCRichWorkspace", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCRichWorkspace", "failed to create property", e) + } + return NCRichWorkspace(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.RICH_WORKSPACE.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCSharees.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCSharees.kt new file mode 100644 index 000000000..4c7957067 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCSharees.kt @@ -0,0 +1,121 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import androidx.annotation.VisibleForTesting +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.shares.ShareeUser +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCSharees private constructor( + val sharees: Array +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCSharees { + // NC sharees property + readArrayNode(parser).let { sharees -> + return NCSharees(sharees.toTypedArray()) + } + } + + @Throws(IOException::class, XmlPullParserException::class) + @VisibleForTesting + fun readArrayNode(parser: XmlPullParser): List { + var list: List = emptyList() + + val depth = parser.depth + var eventType = parser.eventType + while (eventType != XmlPullParser.END_TAG || parser.depth != depth) { + if (eventType != XmlPullParser.TEXT) { + list = readNCSharees(parser) + } + if (parser.eventType == XmlPullParser.END_TAG && parser.depth == depth) { + return list + } + + eventType = parser.next() + } + + return list + } + + private fun readNCSharees(parser: XmlPullParser): List { + val list: ArrayList = ArrayList() + + val depth = parser.depth + var eventType = parser.eventType + while (eventType != XmlPullParser.END_TAG || parser.depth != depth) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + // only add non-null users + readNCSharee(parser)?.let { list.add(it) } + } + + eventType = parser.next() + } + + return list + } + + private fun readNCSharee(parser: XmlPullParser): ShareeUser? { + val depth = parser.depth + var eventType = parser.eventType + + var userId: String? = null + var displayName: String? = null + var shareType: ShareType? = null + + while (eventType != XmlPullParser.END_TAG || parser.depth != depth) { + if (eventType != XmlPullParser.TEXT) { + when (parser.propertyName()) { + ExtendedProperties.SHAREES_ID.toPropertyName() -> { + userId = readText(parser) + } + + ExtendedProperties.SHAREES_DISPLAY_NAME.toPropertyName() -> { + displayName = readText(parser) + } + + ExtendedProperties.SHAREES_SHARE_TYPE.toPropertyName() -> { + shareType = ShareType.fromValue(readText(parser)?.toInt() ?: 0) + } + } + } + + eventType = parser.next() + } + + // check that user is an actual sharee - e.g. exclude link shares + + val isSupportedShareType = + ShareType.EMAIL == shareType || + ShareType.FEDERATED == shareType || + ShareType.GROUP == shareType || + ShareType.ROOM == shareType + + return if (userId.isNullOrEmpty() || (displayName.isNullOrEmpty() && !isSupportedShareType)) { + null + } else { + ShareeUser(userId, displayName, shareType) + } + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.SHAREES.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTags.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTags.kt new file mode 100644 index 000000000..c7d861d0c --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTags.kt @@ -0,0 +1,107 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import androidx.annotation.VisibleForTesting +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.propertyName +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import com.owncloud.android.lib.resources.tags.Tag +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCTags private constructor( + val tags: Array +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): NCTags { + // NC tags property + readArrayNode(parser).let { tags -> + return NCTags(tags.toTypedArray()) + } + } + + @Throws(IOException::class, XmlPullParserException::class) + @VisibleForTesting + fun readArrayNode(parser: XmlPullParser): List { + var list: List = emptyList() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType != XmlPullParser.TEXT) { + list = readNCTags(parser) + } + if (parser.eventType == XmlPullParser.END_TAG && parser.depth == depth) { + return list + } + + eventType = parser.next() + } + + return list + } + + private fun readNCTags(parser: XmlPullParser): List { + val list: ArrayList = ArrayList() + + val depth = parser.depth + var eventType = parser.eventType + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType == XmlPullParser.START_TAG && parser.depth == depth + 1) { + list.add(readNCTag(parser)) + } + + eventType = parser.next() + } + + return list + } + + private fun readNCTag(parser: XmlPullParser): Tag { + val depth = parser.depth + var eventType = parser.eventType + + var id = "" + var name = "" + var color = "" + + while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) { + if (eventType != XmlPullParser.TEXT) { + when (parser.propertyName().toString()) { + "http://nextcloud.org/ns:id" -> { + id = readText(parser).orEmpty() + } + + "http://nextcloud.org/ns:name" -> { + name = readText(parser).orEmpty() + } + + "http://nextcloud.org/ns:color" -> { + color = readText(parser).orEmpty() + } + } + } + + eventType = parser.next() + } + + return Tag(id, name, color) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.SYSTEM_TAGS.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinDeletionTime.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinDeletionTime.kt new file mode 100644 index 000000000..f69ae649b --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinDeletionTime.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCTrashbinDeletionTime private constructor( + val deletionTime: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCTrashbinDeletionTime(text.toLong()) + } + } catch (e: IOException) { + Log.e("NCTrashbinDeletionTime", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCTrashbinDeletionTime", "failed to create property", e) + } + return NCTrashbinDeletionTime(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.TRASHBIN_DELETION_TIME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinFilename.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinFilename.kt new file mode 100644 index 000000000..2ece1305e --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinFilename.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCTrashbinFilename private constructor( + val originalLocation: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCTrashbinFilename(text) + } + } catch (e: IOException) { + Log.e("NCTrashbinFilename", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCTrashbinFilename", "failed to create property", e) + } + return NCTrashbinFilename(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.TRASHBIN_FILENAME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinLocation.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinLocation.kt new file mode 100644 index 000000000..693d078b0 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCTrashbinLocation.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCTrashbinLocation private constructor( + val originalLocation: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return NCTrashbinLocation(text) + } + } catch (e: IOException) { + Log.e("NCTrashbinLocation", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCTrashbinLocation", "failed to create property", e) + } + return NCTrashbinLocation(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.TRASHBIN_ORIGINAL_LOCATION.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCUploadTime.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCUploadTime.kt new file mode 100644 index 000000000..3255e464b --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/NCUploadTime.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class NCUploadTime private constructor( + val uploadTime: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = XmlUtils.readText(parser) + if (!text.isNullOrEmpty()) { + return NCUploadTime(text.toLong()) + } + } catch (e: IOException) { + Log.e("NCUploadTime", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("NCUploadTime", "failed to create property", e) + } + return NCUploadTime(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.UPLOAD_TIME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCCommentsUnread.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCCommentsUnread.kt new file mode 100644 index 000000000..53d12601a --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCCommentsUnread.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCCommentsUnread private constructor( + val commentsCount: Int +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCCommentsUnread(text.toInt()) + } + } catch (e: IOException) { + Log.e("OCCommentsUnread", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCCommentsUnread", "failed to create property", e) + } + return OCCommentsUnread(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.UNREAD_COMMENTS.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCDisplayName.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCDisplayName.kt new file mode 100644 index 000000000..9d2759927 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCDisplayName.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCDisplayName private constructor( + val displayName: String +) : Property { + class Factory : PropertyFactory { + override fun getName(): Property.Name = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCDisplayName(text) + } + } catch (e: IOException) { + Log.e("OCDisplayName", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCDisplayName", "failed to create property", e) + } + return OCDisplayName("") + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.DISPLAY_NAME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCId.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCId.kt new file mode 100644 index 000000000..dc2217cc9 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCId.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCId private constructor( + val id: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCId(text) + } + } catch (e: IOException) { + Log.e("OCId", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCId", "failed to create property", e) + } + return OCId(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.NAME_REMOTE_ID.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCLocalId.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCLocalId.kt new file mode 100644 index 000000000..c2938c651 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCLocalId.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCLocalId private constructor( + val localId: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCLocalId(text.toLong()) + } + } catch (e: IOException) { + Log.e("OCLocalId", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCLocalId", "failed to create property", e) + } + return OCLocalId(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.NAME_LOCAL_ID.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCOwnerDisplayName.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCOwnerDisplayName.kt new file mode 100644 index 000000000..c3203e36a --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCOwnerDisplayName.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCOwnerDisplayName private constructor( + val ownerDisplayName: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCOwnerDisplayName(text) + } + } catch (e: IOException) { + Log.e("OCOwnerDisplayName", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCOwnerDisplayName", "failed to create property", e) + } + return OCOwnerDisplayName(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.OWNER_DISPLAY_NAME.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCOwnerId.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCOwnerId.kt new file mode 100644 index 000000000..510c04960 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCOwnerId.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCOwnerId private constructor( + val ownerId: String? +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCOwnerId(text) + } + } catch (e: IOException) { + Log.e("OCOwnerId", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCOwnerId", "failed to create property", e) + } + return OCOwnerId(null) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.OWNER_ID.toPropertyName() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCSize.kt b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCSize.kt new file mode 100644 index 000000000..50fa014a1 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/files/webdav/OCSize.kt @@ -0,0 +1,44 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Tobias Kaminsky + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.files.webdav + +import android.util.Log +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.PropertyFactory +import at.bitfire.dav4jvm.XmlUtils.readText +import com.owncloud.android.lib.common.network.ExtendedProperties +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import java.io.IOException + +class OCSize private constructor( + val size: Long +) : Property { + class Factory : PropertyFactory { + override fun getName() = NAME + + override fun create(parser: XmlPullParser): Property { + try { + val text = readText(parser) + if (!text.isNullOrEmpty()) { + return OCSize(text.toLong()) + } + } catch (e: IOException) { + Log.e("OCSize", "failed to create property", e) + } catch (e: XmlPullParserException) { + Log.e("OCSize", "failed to create property", e) + } + return OCSize(0) + } + } + + companion object { + @JvmField + val NAME = ExtendedProperties.NAME_SIZE.toPropertyName() + } +}