├── .github └── workflows │ └── build.yaml ├── .gitignore ├── Changelog.md ├── LICENSE ├── README.developers.md ├── README.md ├── nb-configuration.xml ├── nbactions.xml ├── pom.xml └── src ├── main ├── java-templates │ └── Version.java ├── java │ └── org │ │ └── aarboard │ │ └── nextcloud │ │ └── api │ │ ├── AuthenticationConfig.java │ │ ├── NextcloudConnector.java │ │ ├── ServerConfig.java │ │ ├── config │ │ ├── AppConfigAppKeyValueAnswer.java │ │ ├── AppConfigAppsAnswer.java │ │ └── ConfigConnector.java │ │ ├── exception │ │ ├── MoreThanOneShareFoundException.java │ │ ├── NextcloudApiException.java │ │ └── NextcloudOperationFailedException.java │ │ ├── filesharing │ │ ├── FilesharingConnector.java │ │ ├── ItemType.java │ │ ├── Share.java │ │ ├── SharePermissions.java │ │ ├── ShareType.java │ │ ├── SharesXMLAnswer.java │ │ └── SingleShareXMLAnswer.java │ │ ├── provisioning │ │ ├── GroupListAnswer.java │ │ ├── ProvisionConnector.java │ │ ├── Quota.java │ │ ├── QuotaDeserializer.java │ │ ├── ShareData.java │ │ ├── User.java │ │ ├── UserData.java │ │ ├── UserDetailsAnswer.java │ │ ├── UserDetailsListAnswer.java │ │ └── UserListAnswer.java │ │ ├── utils │ │ ├── ConnectorCommon.java │ │ ├── InstantXmlAdapter.java │ │ ├── JsonAnswer.java │ │ ├── JsonAnswerParser.java │ │ ├── JsonListAnswer.java │ │ ├── JsonVoidAnswer.java │ │ ├── ListXMLAnswer.java │ │ ├── LocalDateXmlAdapter.java │ │ ├── NextcloudResponse.java │ │ ├── NextcloudResponseHelper.java │ │ ├── NextcloudSearch.java │ │ ├── WebdavInputStream.java │ │ ├── XMLAnswer.java │ │ └── XMLAnswerParser.java │ │ └── webdav │ │ ├── AWebdavHandler.java │ │ ├── Files.java │ │ ├── Folders.java │ │ ├── ResourceProperties.java │ │ └── pathresolver │ │ ├── NextcloudVersion.java │ │ ├── PathHelper.java │ │ ├── WebDavPathResolver.java │ │ ├── WebDavPathResolverBuilder.java │ │ └── WebDavpathResolverVersionImpl.java └── resources │ └── org │ └── aarboard │ └── nextcloud │ └── api │ └── webdav │ └── pathresolver │ ├── webdavpathresolver_14.properties │ └── webdavpathresolver_20.properties └── test ├── java └── org │ └── aarboard │ └── nextcloud │ └── api │ ├── ATestClass.java │ ├── TestConfigConnector.java │ ├── TestFiles.java │ ├── TestFolders.java │ ├── TestHelper.java │ ├── TestUserGroupAdmin.java │ ├── WebDavPathResolverBuilderTest.java │ └── filesharing │ └── FilesharingConnectorTest.java ├── resources └── test6.txt └── testdata └── test5.txt /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | # Triggers the workflow on push for the main and develop branch 8 | push: 9 | branches: [ main, develop ] 10 | pull_request: 11 | branches: [ main, develop ] 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | packages: write 19 | 20 | steps: 21 | - uses: actions/checkout@v5 22 | with: 23 | ref: ${{ github.ref }} 24 | - uses: actions/setup-java@v5 25 | with: 26 | java-version: 11 27 | distribution: 'zulu' 28 | architecture: x64 29 | 30 | - name: Build with Maven 31 | run: mvn -B package --file pom.xml 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | /target/ 14 | /nbproject/ 15 | 16 | ### Vagrant ### 17 | .vagrant/ 18 | /.idea/ 19 | /nextcloud-api.iml 20 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog for nextcloud api 2 | 3 | ## Version 14.0.0 4 | - 2025-10-21 5 | - Bump required java version from 8 to 11+ (Thanks to kindlich) 6 | - Make connection autoclosable (thanks to raboof) 7 | - Bump pom.xml dependencies where possible 8 | 9 | ## Version 13.0.2 10 | - 2024-03-16 11 | - Typo fixes 12 | - Javadoc url's 13 | - Modifiers should be declared in the correct order 14 | - Try-with resources 15 | - Use constants where possible 16 | - Removed redundant Exception throwing 17 | - Simplified assertions in tests 18 | 19 | ## Version 13.0.1 20 | - 2023-09-29 21 | - Release 13.0.1 22 | - Cleanup closed http client, thanks to lucnygr 23 | 24 | ## Version 13 25 | - 2023-09-29 26 | - Release 13.0.0 27 | - Switch to jakarta xml stuff for simpler Java 11+ compatibility 28 | - The api remains identical, except the places where javax.xml stuff was exposed 29 | They now use the jakarta.xml names 30 | - Bump all dependencies to latest versions, also build environment 31 | - Added Cyclone DX SBOM 32 | 33 | ## Version 12 34 | - 2023-09-29 35 | - Release 12.0.5, added bearer authentication for non-webdav calls (Thanks to Arnout Engelen) 36 | - 2022-11-29 37 | - Release 12.0.4, upgraded indirect commons-codec dependency 38 | - Release 12.0.3, upgraded jackson dependencies 39 | - 2022-04-01 40 | - Release 12.0.2, upgraded jaxb-runtime to 3.0.2 too because of indirect dependencies 41 | - 2022-04-01 42 | - Release 12.0.1, upgraded various dependencies 43 | - jackson-databind upgraded to 2.13.2.2 to fix CVE-2020-36518 (Thanks to MrRoubous) 44 | - 2021-11-02 45 | - Release 12.0.0, thanks for all contributions (See below) 46 | - 2021-10-28 47 | - Bump version to 12.0.0-SNAPSHOT due to api changes 48 | - We did remove all xml based provision api, since they don't work 49 | in many cases, we use JSON now 50 | - The getQuota() method now returns an Optional, to handle the case 51 | when no quota is set at all (Allowing unlimited storage) 52 | Thanks @kriszman for the patches 53 | 54 | ## Version 11 55 | - 2021-10-13 56 | - Prepare 11.7.0-SNAPSHOT 57 | - Fix downloadFolder() when having special chars in folder name(s) 58 | Thanks to flelayo (Issue #71) 59 | - Adding bearer authentication (Constructors got lost in merge) 60 | - Added support for user quota field, pull request #70, thanks to kriszman 61 | - Fix downloaded file name if URI encoded values are returned, use request file name 62 | Thanks to flelayo for the fix to issue #69 63 | - 2021-10-04 64 | - Release 11.6.0 65 | - Merged the webdav path resolver from thestomprock with some fixups 66 | - 2021-10-03 67 | - Added bearer authentication, thanks torshid 68 | - Base path customization, thanks torshid 69 | - 2021-09-09 70 | - Release 11.5.1 71 | - Integrated fix for invalid userlist, pull request #62, Thanks kriszman 72 | - 2021-05-20 73 | - Added rename/move operation for folders and files 74 | - Version bump to 11.5.0 because of added api methods 75 | - 2021-04-27 76 | - Updated various used libraries 77 | - 2020-11-21 78 | - Added methods to access nextcloud instance installed in subfolders (Thanks to helmut8080) 79 | - Version bump to 11.4.0 because of added api methods 80 | - 2020-07-15 81 | - Enanced API to retrieve file and folder meta data (properties) 82 | - Added Version class so you can get the library version and buils infos at runtime 83 | - 2020-07-14 84 | - Added API to upload File objects 85 | - Deprecated the API to upload InputStream, due to some potential server problems 86 | - Added API to retrieve file meta data 87 | - 2020-07-11 88 | - Added jakarta xml bind, since xml bind is no longer existing in java 11 89 | - 2020-05-11 90 | - Added methods to access the application config api (Thanks to col-panic) 91 | - Version bump to 11.3.0 because of added api methods 92 | - 2020-05-05 93 | - Added option to return full path to files in folder listings (Thanks to thepivo) 94 | - Added option to use the continue header in file uploads/puts (Thanks to TobiWineKenobi) 95 | - Version bump to 11.2.0 because of added api methods 96 | - 2020-02-24 97 | - Added option to only return files in folder listings (Thanks to SimonIT) 98 | - Upgraded slf4j to 1.7.30, httpclient to 4.5.11, httpcore to 4.4.13 99 | - Integrated pull request from col-panic for clean shutdown and directory install support 100 | - Release 11.1.0 101 | - 2019-09-07 102 | - Switch to slf4j as logging framework 103 | - fix to also use port in sardine connector 104 | - Upgraded to sardine 5.9 105 | - Added explicit dependencies to http core and http client libraries 106 | - 2018-12-03 107 | - downloadFile now can return an InputStream 108 | - Bump version to 11.1.0-SNAPSHOT to match semantic versioning 109 | - Prepare for next dev cycle 11.0.4-SNAPSHOT 110 | - 2018-12-03 111 | - Release 11.0.3 112 | - Upgrade httpasyncclient to 4.1.4 113 | - Correctly encode URL's in Files and Folder connector 114 | - Use port specs in sharing connector 115 | - Added E-Mail share type 116 | - 2018-08-03 117 | - Release 11.0.2 118 | - Reworked file/folder handling 119 | - 2018-07-25 120 | - Release 11.0.1 121 | - Available via Maven central 122 | - 2018-07-25 123 | - Added to maven central to simplify usage 124 | - 2018-06-14 125 | - Method added to download files 126 | - 2017-08-19 127 | - Added feature to recursive folder handlings 128 | - 2017-06-08 129 | - XML parsing via JAXB, REST user provisioning and share api added 130 | - 2017-05-29 131 | - Async method calls 132 | - 2017-05-22 133 | - Improved Exception generation/handling, user create/delete implemented 134 | - 2017-05-09 135 | - Implemented fileupload 136 | - 2017-03-30 137 | - Initial release 138 | 139 | (c) André Schild, Aarboard AG www.aarboard.ch 140 | 141 | -------------------------------------------------------------------------------- /README.developers.md: -------------------------------------------------------------------------------- 1 | # nextcloud-java-api contributors infos 2 | Java api library to access nextcloud features from java applications 3 | 4 | ## Versioning 5 | Starting with 11.1.0 we use semanic versioning according to 6 | https://semver.org/spec/v2.0.0.html 7 | 8 | In short: 9 | Given a version number MAJOR.MINOR.PATCH, increment the: 10 | 11 | MAJOR version when you make incompatible API changes, 12 | MINOR version when you add functionality in a backwards compatible manner, and 13 | PATCH version when you make backwards compatible bug fixes. 14 | 15 | Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. 16 | 17 | ## Changelog 18 | If enhancing the code base, please also update the [Changelog](Changelog.md) 19 | 20 | ## Unit tests 21 | For all new functionality, please provide a unit test. 22 | 23 | For the unit test to be executed, you need to specify valid 24 | next cloud server name and login admin credentials 25 | 26 | You can specify them in your settings.xml file in this way: 27 | ``` XML 28 | 29 | 30 | 31 | nextcloud.api.test 32 | 33 | true 34 | 35 | 36 | test.nextcloud.org 37 | 444 38 | admin 39 | adminp@ssw0rd 40 | 41 | 42 | 43 | 44 | 45 | nextcloud.api.test 46 | 47 | 48 | ``` 49 | 50 | 51 | (c) André Schild, Aarboard AG www.aarboard.ch 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nextcloud-java-api 2 | Java api library to access nextcloud features from java applications 3 | 4 | ![Sonatype Nexus (Releases)](https://img.shields.io/nexus/r/org.aarboard.nextcloud/nextcloud-api?label=release&nexusVersion=2&server=https%3A%2F%2Foss.sonatype.org%2F) 5 | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/org.aarboard.nextcloud/nextcloud-api?label=snapshot&server=https%3A%2F%2Foss.sonatype.org%2F) 6 | 7 | ## What is the nextcloud api library? 8 | > Use nextcloud features from inside your java application 9 | 10 | ## Currently implemented features 11 | - Management of groups 12 | - Folder management (Without access control) 13 | - List shares and create new file shares (No way to delete/update shares atm.) 14 | - Tested against nextCloud 27.0.1 server version, but should also work with older nextCloud and ownCloud systems 15 | 16 | ## Usage 17 | - Add this dependency to your pom.xml file 18 | ``` 19 | 20 | com.github.a-schild 21 | nextcloud-java-api 22 | 14.0.0 23 | 24 | ``` 25 | 26 | - The 14.x versions require Java 11+,as the jakarta.xml binding requires Java 11+ 27 | - The 13.x versions are now using the jakarta.xml binding stuff, to prevent problems with Java 11+ 28 | No API changes have been made in v13, but at some places the XML stuff is exposed 29 | Which made it necessary to bump the major version number 30 | - Create a NextcloudConnector instance and provide your server settings and authentification 31 | - Now you can use the methods exposed to access your nextcloud instance 32 | 33 | ## When you wish to contribute to the project 34 | [Infos for contributors](./README.developers.md) 35 | 36 | ## Changelog 37 | [Changelog](Changelog.md) 38 | 39 | 40 | (c) André Schild, Aarboard AG www.aarboard.ch 41 | -------------------------------------------------------------------------------- /nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | gpl30 17 | true 18 | JDK_1.8_ZULU 19 | 20 | 21 | -------------------------------------------------------------------------------- /nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CUSTOM-Deploy 5 | Deploy 6 | 7 | deploy 8 | -DskipTests 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java-templates/Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | /** 20 | * 21 | * @author a.schild 22 | */ 23 | public final class Version { 24 | 25 | private static final String VERSION = "${project.version}"; 26 | private static final String GROUPID = "${project.groupId}"; 27 | private static final String ARTIFACTID = "${project.artifactId}"; 28 | private static final String REVISION = "${buildNumber}"; 29 | 30 | public static String getVersion() { 31 | return getVERSION(); 32 | } 33 | 34 | /** 35 | * @return the VERSION 36 | */ 37 | public static String getVERSION() { 38 | return VERSION; 39 | } 40 | 41 | /** 42 | * @return the GROUPID 43 | */ 44 | public static String getGROUPID() { 45 | return GROUPID; 46 | } 47 | 48 | /** 49 | * @return the ARTIFACTID 50 | */ 51 | public static String getARTIFACTID() { 52 | return ARTIFACTID; 53 | } 54 | 55 | /** 56 | * @return the REVISION 57 | */ 58 | public static String getREVISION() { 59 | return REVISION; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/AuthenticationConfig.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api; 2 | 3 | public class AuthenticationConfig { 4 | 5 | private String loginName; 6 | private String password; 7 | 8 | private String bearerToken; 9 | 10 | public AuthenticationConfig(String loginName, String password) { 11 | this.loginName = loginName; 12 | this.password = password; 13 | } 14 | 15 | public AuthenticationConfig(String bearerToken) { 16 | this.bearerToken = bearerToken; 17 | } 18 | 19 | public String getLoginName() { 20 | return loginName; 21 | } 22 | 23 | public void setLoginName(String loginName) { 24 | this.loginName = loginName; 25 | } 26 | 27 | public String getPassword() { 28 | return password; 29 | } 30 | 31 | public void setPassword(String password) { 32 | this.password = password; 33 | } 34 | 35 | public String getBearerToken() { 36 | return bearerToken; 37 | } 38 | 39 | public void setBearerToken(String bearerToken) { 40 | this.bearerToken = bearerToken; 41 | } 42 | 43 | public boolean usesBasicAuthentication() { 44 | return bearerToken == null; 45 | } 46 | 47 | public boolean usesBearerTokenAuthentication() { 48 | return !usesBasicAuthentication(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/ServerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | /** 20 | * 21 | * @author a.schild 22 | */ 23 | public class ServerConfig { 24 | 25 | private AuthenticationConfig authenticationConfig; 26 | private String serverName; 27 | private String subPathPrefix; 28 | private boolean useHTTPS; 29 | private int port; 30 | private boolean trustAllCertificates; 31 | 32 | /** 33 | * Use this constructor if your nextcloud instance is installed in the 34 | * root of the webhosting, like ... 35 | * 36 | * @param serverName ip or dns name of server 37 | * @param useHTTPS Use https or http to connect 38 | * @param port Port, usually 443 for https and 80 for http 39 | * @param loginName 40 | * @param password 41 | */ 42 | public ServerConfig(String serverName, 43 | boolean useHTTPS, 44 | int port, 45 | String loginName, 46 | String password) { 47 | this.authenticationConfig = new AuthenticationConfig(loginName, password); 48 | this.serverName = serverName; 49 | this.subPathPrefix = null; 50 | this.useHTTPS = useHTTPS; 51 | this.port = port; 52 | this.trustAllCertificates = false; 53 | } 54 | 55 | /** 56 | * Use this constructor if your nextcloud instance is installed in the 57 | * root of the webhosting, like ... 58 | * 59 | * @param serverName ip or dns name of server 60 | * @param useHTTPS Use https or http to connect 61 | * @param port Port, usually 443 for https and 80 for http 62 | * @param authenticationConfig Authentication configuration for authentication 63 | */ 64 | public ServerConfig(String serverName, 65 | boolean useHTTPS, 66 | int port, 67 | AuthenticationConfig authenticationConfig) { 68 | this.authenticationConfig = authenticationConfig; 69 | this.serverName = serverName; 70 | this.subPathPrefix = null; 71 | this.useHTTPS = useHTTPS; 72 | this.port = port; 73 | this.trustAllCertificates = false; 74 | } 75 | 76 | /** 77 | * Is this constructor if your nextcloud is installed in a subfolder of the server 78 | * like ...nextcloud/ 79 | * 80 | * @param serverName ip or dns name of server 81 | * @param useHTTPS Use https or http to connect 82 | * @param port Port, usually 443 for https and 80 for http 83 | * @param subPathPrefix Path to your nextcloud instance, without starting and trailing / 84 | * can be null if installed in root 85 | * @param authenticationConfig Authentication configuration for authentication 86 | */ 87 | public ServerConfig( 88 | String serverName, 89 | boolean useHTTPS, 90 | int port, 91 | String subPathPrefix, 92 | AuthenticationConfig authenticationConfig) { 93 | this.authenticationConfig = authenticationConfig; 94 | this.serverName = serverName; 95 | this.subPathPrefix = subPathPrefix; 96 | this.useHTTPS = useHTTPS; 97 | this.port = port; 98 | this.trustAllCertificates = false; 99 | } 100 | 101 | /** 102 | * @return the authenticationConfig 103 | */ 104 | public AuthenticationConfig getAuthenticationConfig() { 105 | return authenticationConfig; 106 | } 107 | 108 | /** 109 | * @param authenticationConfig authenticationConfig to set 110 | */ 111 | public void setAuthenticationConfig(AuthenticationConfig authenticationConfig) { 112 | this.authenticationConfig = authenticationConfig; 113 | } 114 | 115 | /** 116 | * @return the serverName 117 | */ 118 | public String getServerName() { 119 | return serverName; 120 | } 121 | 122 | /** 123 | * @param serverName 124 | * the serverName to set, defaults to null 125 | */ 126 | public void setServerName(String serverName){ 127 | this.serverName = serverName; 128 | } 129 | 130 | /** 131 | * @deprecated Use getSubPathPrefix() instead 132 | * @return the configured subpath prefix 133 | */ 134 | @Deprecated 135 | public String getSubpathPrefix(){ 136 | return getSubPathPrefix(); 137 | } 138 | 139 | /** 140 | * @return the configured sub path prefix 141 | */ 142 | public String getSubPathPrefix(){ 143 | return subPathPrefix; 144 | } 145 | 146 | /** 147 | * @deprecated Use setSubPathPrefix() instead 148 | * @param subpathPrefix to apply 149 | */ 150 | @Deprecated 151 | public void setSubpathPrefix(String subpathPrefix){ 152 | setSubPathPrefix(subpathPrefix); 153 | } 154 | 155 | /** 156 | * @param subPathPrefix to apply 157 | */ 158 | public void setSubPathPrefix(String subPathPrefix){ 159 | this.subPathPrefix = subPathPrefix; 160 | } 161 | 162 | /** 163 | * @return the useHTTPS 164 | */ 165 | public boolean isUseHTTPS() { 166 | return useHTTPS; 167 | } 168 | 169 | /** 170 | * @param useHTTPS the useHTTPS to set 171 | */ 172 | public void setUseHTTPS(boolean useHTTPS) { 173 | this.useHTTPS = useHTTPS; 174 | } 175 | 176 | /** 177 | * @return the port 178 | */ 179 | public int getPort() { 180 | return port; 181 | } 182 | 183 | /** 184 | * @param port the port to set 185 | */ 186 | public void setPort(int port) { 187 | this.port = port; 188 | } 189 | 190 | /** 191 | * @param trustAllCertificates if the client should accept any 192 | * HTTPScertificate (e.g. to work against a self-signed 193 | * certificate) 194 | */ 195 | public void setTrustAllCertificates(boolean trustAllCertificates){ 196 | this.trustAllCertificates = trustAllCertificates; 197 | } 198 | 199 | /** 200 | * @return if the client should accept any HTTPS certificate (e.g. to work against a self-signed 201 | * certificate) 202 | */ 203 | public boolean isTrustAllCertificates(){ 204 | return trustAllCertificates; 205 | } 206 | 207 | /** 208 | * 209 | * @return username of the given user (Is not always the login name) 210 | */ 211 | public String getUserName() 212 | { 213 | // TODO: We need to dynamically lookup the username 214 | // from the server, via https://serverocs/v1.php/cloud/user 215 | return authenticationConfig.getLoginName(); 216 | } 217 | 218 | /** 219 | * 220 | * @return login name of the given user (Is not always the username) 221 | */ 222 | public String getLoginName() 223 | { 224 | return authenticationConfig.getLoginName(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/config/AppConfigAppKeyValueAnswer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Marco Descher 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.config; 18 | 19 | 20 | import jakarta.xml.bind.annotation.XmlAccessType; 21 | import jakarta.xml.bind.annotation.XmlAccessorType; 22 | import jakarta.xml.bind.annotation.XmlElement; 23 | import jakarta.xml.bind.annotation.XmlRootElement; 24 | import org.aarboard.nextcloud.api.utils.XMLAnswer; 25 | 26 | @XmlRootElement(name = "ocs") 27 | public class AppConfigAppKeyValueAnswer extends XMLAnswer { 28 | private Data data; 29 | 30 | public String getAppConfigAppKeyValue(){ 31 | return data.getAppConfigAppKeyValue; 32 | } 33 | 34 | @XmlAccessorType(XmlAccessType.FIELD) 35 | private static final class Data { 36 | @XmlElement(name = "data") 37 | private String getAppConfigAppKeyValue; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/config/AppConfigAppsAnswer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Marco Descher 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.config; 18 | 19 | import jakarta.xml.bind.annotation.XmlAccessType; 20 | import jakarta.xml.bind.annotation.XmlAccessorType; 21 | import jakarta.xml.bind.annotation.XmlElement; 22 | import jakarta.xml.bind.annotation.XmlElementWrapper; 23 | import jakarta.xml.bind.annotation.XmlRootElement; 24 | import java.util.List; 25 | import org.aarboard.nextcloud.api.utils.XMLAnswer; 26 | 27 | @XmlRootElement(name = "ocs") 28 | public class AppConfigAppsAnswer extends XMLAnswer 29 | { 30 | private Data data; 31 | 32 | public List getAppConfigApps() 33 | { 34 | return data.appConfigApps; 35 | } 36 | 37 | @XmlAccessorType(XmlAccessType.FIELD) 38 | private static final class Data 39 | { 40 | @XmlElementWrapper(name = "data") 41 | @XmlElement(name = "element") 42 | private List appConfigApps; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/config/ConfigConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Marco Descher 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.config; 18 | 19 | import java.util.Collections; 20 | import java.util.LinkedList; 21 | import java.util.List; 22 | import java.util.concurrent.CompletableFuture; 23 | 24 | import org.aarboard.nextcloud.api.ServerConfig; 25 | import org.aarboard.nextcloud.api.utils.ConnectorCommon; 26 | import org.aarboard.nextcloud.api.utils.NextcloudResponseHelper; 27 | import org.aarboard.nextcloud.api.utils.XMLAnswerParser; 28 | import org.apache.http.NameValuePair; 29 | import org.apache.http.message.BasicNameValuePair; 30 | 31 | public class ConfigConnector { 32 | 33 | private static final String CONFIG_PART = "ocs/v2.php/apps/provisioning_api/api/v1/config/apps"; 34 | 35 | private final ConnectorCommon connectorCommon; 36 | 37 | public ConfigConnector(ServerConfig serverConfig){ 38 | this.connectorCommon = new ConnectorCommon(serverConfig); 39 | } 40 | 41 | public List getAppConfigApps(){ 42 | return NextcloudResponseHelper.getAndWrapException(getAppConfigAppsAsync()) 43 | .getAppConfigApps(); 44 | } 45 | 46 | private CompletableFuture getAppConfigAppsAsync(){ 47 | return connectorCommon.executeGet(CONFIG_PART, Collections.emptyList(), 48 | XMLAnswerParser.getInstance(AppConfigAppsAnswer.class)); 49 | } 50 | 51 | public List getAppConfigAppKeys(String appConfigApp){ 52 | return NextcloudResponseHelper.getAndWrapException(getAppConfigAppsAsync(appConfigApp)) 53 | .getAppConfigApps(); 54 | } 55 | 56 | private CompletableFuture getAppConfigAppsAsync(String appConfigApp){ 57 | return connectorCommon.executeGet(CONFIG_PART + "/" + appConfigApp, 58 | Collections.emptyList(), XMLAnswerParser.getInstance(AppConfigAppsAnswer.class)); 59 | } 60 | 61 | public String getAppConfigAppKeyValue(String appConfigApp, String appConfigAppKey){ 62 | return NextcloudResponseHelper 63 | .getAndWrapException(getAppConfigAppsKeyAsync(appConfigApp + "/" + appConfigAppKey)) 64 | .getAppConfigAppKeyValue(); 65 | } 66 | 67 | public String getAppConfigAppKeyValue(String appConfigAppKeyPath){ 68 | return NextcloudResponseHelper 69 | .getAndWrapException(getAppConfigAppsKeyAsync(appConfigAppKeyPath)) 70 | .getAppConfigAppKeyValue(); 71 | } 72 | 73 | private CompletableFuture getAppConfigAppsKeyAsync( 74 | String appConfigAppKeyPath){ 75 | return connectorCommon.executeGet(CONFIG_PART + "/" + appConfigAppKeyPath, 76 | Collections.emptyList(), XMLAnswerParser.getInstance(AppConfigAppKeyValueAnswer.class)); 77 | } 78 | 79 | public boolean setAppConfigAppKeyValue(String appConfigApp, String appConfigAppKey, 80 | Object value){ 81 | return NextcloudResponseHelper.isStatusCodeOkay( 82 | setAppConfigAppKeyValueAsync(appConfigApp + "/" + appConfigAppKey, value)); 83 | } 84 | 85 | public boolean setAppConfigAppKeyValue(String appConfigAppKeyPath, Object value){ 86 | return NextcloudResponseHelper 87 | .isStatusCodeOkay(setAppConfigAppKeyValueAsync(appConfigAppKeyPath, value)); 88 | } 89 | 90 | public CompletableFuture setAppConfigAppKeyValueAsync( 91 | String appConfigAppKeyPath, Object value){ 92 | List postParams = new LinkedList<>(); 93 | postParams.add(new BasicNameValuePair("value", value.toString())); 94 | return connectorCommon.executePost(CONFIG_PART + "/" + appConfigAppKeyPath, postParams, 95 | XMLAnswerParser.getInstance(AppConfigAppKeyValueAnswer.class)); 96 | } 97 | 98 | public boolean deleteAppConfigAppKeyEntry(String appConfigApp, String appConfigAppkey){ 99 | throw new UnsupportedOperationException(); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/exception/MoreThanOneShareFoundException.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.exception; 2 | 3 | public class MoreThanOneShareFoundException extends NextcloudApiException { 4 | private static final long serialVersionUID = 5654006062204752474L; 5 | 6 | public MoreThanOneShareFoundException(int shareId) { 7 | super(String.format("More than one share found, not possible <%d>", shareId)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/exception/NextcloudApiException.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.exception; 2 | 3 | public class NextcloudApiException extends RuntimeException { 4 | private static final long serialVersionUID = 8088239559973590632L; 5 | 6 | public NextcloudApiException(Throwable cause) { 7 | super(cause); 8 | } 9 | 10 | public NextcloudApiException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/exception/NextcloudOperationFailedException.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.exception; 2 | 3 | public class NextcloudOperationFailedException extends NextcloudApiException { 4 | private static final long serialVersionUID = 6382478664807826933L; 5 | 6 | public NextcloudOperationFailedException(int statuscode, String message) { 7 | super(String.format("Nextcloud API call failed with statuscode %d and message \"%s\"", statuscode, message)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/FilesharingConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import java.util.Collections; 20 | import java.util.LinkedList; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.concurrent.CompletableFuture; 24 | import java.util.stream.Collectors; 25 | import org.aarboard.nextcloud.api.ServerConfig; 26 | import org.aarboard.nextcloud.api.exception.MoreThanOneShareFoundException; 27 | import org.aarboard.nextcloud.api.provisioning.ShareData; 28 | import org.aarboard.nextcloud.api.utils.ConnectorCommon; 29 | import org.aarboard.nextcloud.api.utils.NextcloudResponseHelper; 30 | import org.aarboard.nextcloud.api.utils.XMLAnswer; 31 | import org.aarboard.nextcloud.api.utils.XMLAnswerParser; 32 | import org.apache.http.NameValuePair; 33 | import org.apache.http.message.BasicNameValuePair; 34 | 35 | /** 36 | * 37 | * @author a.schild 38 | * 39 | * ... 40 | * When specifying paths, you don't have to specify any webdav roots, or 41 | * remote.php ... stuff. Just use the root of your file store directly 42 | * 43 | */ 44 | public class FilesharingConnector 45 | { 46 | 47 | private static final String ROOT_PART= "ocs/v1.php/apps/files_sharing/api/v1/"; 48 | private static final String SHARES_PART= ROOT_PART+"shares"; 49 | 50 | private final ConnectorCommon connectorCommon; 51 | 52 | public FilesharingConnector(ServerConfig serverConfig) { 53 | this.connectorCommon = new ConnectorCommon(serverConfig); 54 | } 55 | 56 | /** 57 | * Get all shares of this user 58 | * 59 | * @return all shares 60 | */ 61 | public List getShares() 62 | { 63 | return getShares(null, false, false); 64 | } 65 | 66 | /** 67 | * Get all shares of this user asynchronously 68 | * 69 | * @return a CompletableFuture containing the result of the operation 70 | */ 71 | public CompletableFuture getSharesAsync() 72 | { 73 | return getSharesAsync(null, false, false); 74 | } 75 | 76 | /** 77 | * Gets all shares from a given file/folder 78 | * 79 | * @param path path to file/folder 80 | * @param reShares returns not only the shares from the current user but all shares from the given file 81 | * @param subShares returns all shares within a folder, given that path defines a folder 82 | * @return matching shares 83 | */ 84 | public List getShares(String path, boolean reShares, boolean subShares) 85 | { 86 | return NextcloudResponseHelper.getAndCheckStatus(getSharesAsync(path,reShares,subShares)).getShares(); 87 | } 88 | 89 | /** 90 | * Gets all shares from a given file/folder asynchronously 91 | * 92 | * @param path path to file/folder 93 | * @param reShares returns not only the shares from the current user but all shares from the given file 94 | * @param subShares returns all shares within a folder, given that path defines a folder 95 | * @return a CompletableFuture containing the result of the operation 96 | */ 97 | public CompletableFuture getSharesAsync(String path, boolean reShares, boolean subShares) 98 | { 99 | List queryParams= new LinkedList<>(); 100 | if (path != null) 101 | { 102 | queryParams.add(new BasicNameValuePair("path", path)); 103 | } 104 | if (reShares) 105 | { 106 | queryParams.add(new BasicNameValuePair("reshares", "true")); 107 | } 108 | if (subShares) 109 | { 110 | queryParams.add(new BasicNameValuePair("subfiles", "true")); 111 | } 112 | return connectorCommon.executeGet(SHARES_PART, queryParams, XMLAnswerParser.getInstance(SharesXMLAnswer.class)); 113 | } 114 | 115 | /** 116 | * Get share info for a single share 117 | * 118 | * @param shareId id of share (Not path of share) 119 | * @return the share if it has been found, otherwise null 120 | */ 121 | public Share getShareInfo(int shareId) 122 | { 123 | SharesXMLAnswer xa= NextcloudResponseHelper.getAndCheckStatus(getShareInfoAsync(shareId)); 124 | if (xa.getShares() == null) 125 | { 126 | return null; 127 | } 128 | else if (xa.getShares().size() == 1) 129 | { 130 | return xa.getShares().get(0); 131 | } 132 | throw new MoreThanOneShareFoundException(shareId); 133 | } 134 | 135 | /** 136 | * Get share info for a single share asynchronously 137 | * 138 | * @param shareId id of share (Not path of share) 139 | * @return a CompletableFuture containing the result of the operation 140 | */ 141 | public CompletableFuture getShareInfoAsync(int shareId) 142 | { 143 | return connectorCommon.executeGet(SHARES_PART+"/"+Integer.toString(shareId), null, XMLAnswerParser.getInstance(SharesXMLAnswer.class)); 144 | } 145 | 146 | /** 147 | * Shares the specified path with the provided parameters 148 | * 149 | * @param path path to the file/folder which should be shared 150 | * @param shareType 0 = user; 1 = group; 3 = public link; 4 = email; 6 = federated cloud share 151 | * @param shareWithUserOrGroupIdOrEmail user / group id / email with which the file should be shared 152 | * @param publicUpload allow public upload to a public shared folder (true/false) 153 | * @param password password to protect public link Share with 154 | * @param permissions 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default: 31, for public shares: 1) 155 | * @return created share on success 156 | */ 157 | public Share doShare( 158 | String path, 159 | ShareType shareType, 160 | String shareWithUserOrGroupIdOrEmail, 161 | Boolean publicUpload, 162 | String password, 163 | SharePermissions permissions) 164 | { 165 | return NextcloudResponseHelper.getAndCheckStatus(doShareAsync(path, shareType, shareWithUserOrGroupIdOrEmail, publicUpload, password, permissions)).getShare(); 166 | } 167 | 168 | /** 169 | * Shares the specified path with the provided parameters asynchronously 170 | * 171 | * @param path path to the file/folder which should be shared 172 | * @param shareType 0 = user; 1 = group; 3 = public link; 4 = email; 6 = federated cloud share 173 | * @param shareWithUserOrGroupIdOrEmail user / group id / email with which the file should be shared 174 | * @param publicUpload allow public upload to a public shared folder (true/false) 175 | * @param password password to protect public link Share with 176 | * @param permissions 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default: 31, for public shares: 1) 177 | * @return a CompletableFuture containing the result of the operation 178 | */ 179 | public CompletableFuture doShareAsync( 180 | String path, 181 | ShareType shareType, 182 | String shareWithUserOrGroupIdOrEmail, 183 | Boolean publicUpload, 184 | String password, 185 | SharePermissions permissions) 186 | { 187 | List postParams= new LinkedList<>(); 188 | postParams.add(new BasicNameValuePair("path", path)); 189 | postParams.add(new BasicNameValuePair("shareType", Integer.toString(shareType.getIntValue()))); 190 | postParams.add(new BasicNameValuePair("shareWith", shareWithUserOrGroupIdOrEmail)); 191 | if (publicUpload != null) 192 | { 193 | postParams.add(new BasicNameValuePair("publicUpload", publicUpload ? "true" : "false")); 194 | } 195 | if (password != null) 196 | { 197 | postParams.add(new BasicNameValuePair("password", password)); 198 | } 199 | if (permissions != null) 200 | { 201 | postParams.add(new BasicNameValuePair("permissions", Integer.toString(permissions.getCurrentPermission()))); 202 | } 203 | 204 | return connectorCommon.executePost(SHARES_PART, postParams, XMLAnswerParser.getInstance(SingleShareXMLAnswer.class)); 205 | } 206 | 207 | /** 208 | * Changes a single attribute of a share 209 | * 210 | * @param shareId unique identifier of the share 211 | * @param key the attribute to change 212 | * @param value the value to set 213 | * @return true if the operation succeeded 214 | */ 215 | public boolean editShare(int shareId, ShareData key, String value) 216 | { 217 | return NextcloudResponseHelper.isStatusCodeOkay(editShareAsync(shareId, key, value)); 218 | } 219 | 220 | /** 221 | * Changes a single attribute of a share asynchronously 222 | * 223 | * @param shareId unique identifier of the share 224 | * @param key the attribute to change 225 | * @param value the value to set 226 | * @return a CompletableFuture containing the result of the operation 227 | */ 228 | public CompletableFuture editShareAsync(int shareId, ShareData key, String value) 229 | { 230 | List queryParams= Collections.singletonList(new BasicNameValuePair(key.parameterName, value)); 231 | return connectorCommon.executePut(SHARES_PART, Integer.toString(shareId), queryParams, XMLAnswerParser.getInstance(XMLAnswer.class)); 232 | } 233 | 234 | /** 235 | * Changes multiple attributes of a share at once 236 | * 237 | * @param shareId unique identifier of the share 238 | * @param values a Map containing the attributes to set 239 | * @return true if the operation succeeded 240 | */ 241 | public boolean editShare(int shareId, Map values) 242 | { 243 | return NextcloudResponseHelper.isStatusCodeOkay(editShareAsync(shareId, values)); 244 | } 245 | 246 | /** 247 | * Changes multiple attributes of a share at once asynchronously 248 | * 249 | * @param shareId unique identifier of the share 250 | * @param values a Map containing the attributes to set 251 | * @return a CompletableFuture containing the result of the operation 252 | */ 253 | public CompletableFuture editShareAsync(int shareId, Map values) { 254 | List queryParams = values.entrySet().stream() 255 | .map(e -> new BasicNameValuePair(e.getKey().parameterName, e.getValue())).collect(Collectors.toList()); 256 | return connectorCommon.executePut(SHARES_PART, Integer.toString(shareId), queryParams, XMLAnswerParser.getInstance(XMLAnswer.class)); 257 | } 258 | 259 | /** 260 | * Deletes a share 261 | * 262 | * @param shareId unique identifier of the share 263 | * @return true if the operation succeeded 264 | */ 265 | public boolean deleteShare(int shareId) 266 | { 267 | return NextcloudResponseHelper.isStatusCodeOkay(deleteShareAsync(shareId)); 268 | } 269 | 270 | /** 271 | * Deletes a share asynchronously 272 | * 273 | * @param shareId unique identifier of the share 274 | * @return a CompletableFuture containing the result of the operation 275 | */ 276 | public CompletableFuture deleteShareAsync(int shareId) 277 | { 278 | return connectorCommon.executeDelete(SHARES_PART, Integer.toString(shareId), null, XMLAnswerParser.getInstance(XMLAnswer.class)); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/ItemType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import jakarta.xml.bind.annotation.XmlEnum; 20 | import jakarta.xml.bind.annotation.XmlEnumValue; 21 | import jakarta.xml.bind.annotation.XmlType; 22 | import java.security.InvalidParameterException; 23 | 24 | /** 25 | * 26 | * @author a.schild 27 | */ 28 | @XmlType 29 | @XmlEnum(String.class) 30 | public enum ItemType { 31 | @XmlEnumValue("folder") FOLDER("folder"), 32 | @XmlEnumValue("file") FILE("file"); 33 | 34 | private final String itemTypeStr; 35 | 36 | private ItemType(String itemTypeStr) { 37 | this.itemTypeStr = itemTypeStr; 38 | } 39 | 40 | public String getItemTypeStr() { 41 | return itemTypeStr; 42 | } 43 | 44 | public static ItemType getItemByName(String name) 45 | { 46 | for (ItemType t : ItemType.values()) 47 | { 48 | if (t.getItemTypeStr().equals(name)) 49 | { 50 | return t; 51 | } 52 | } 53 | throw new InvalidParameterException("Invalid ItemType found <"+name+">"); 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/Share.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import jakarta.xml.bind.annotation.XmlAccessType; 20 | import jakarta.xml.bind.annotation.XmlAccessorType; 21 | import jakarta.xml.bind.annotation.XmlElement; 22 | import jakarta.xml.bind.annotation.adapters.XmlAdapter; 23 | import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 24 | import java.time.Instant; 25 | import java.time.LocalDate; 26 | import org.aarboard.nextcloud.api.utils.InstantXmlAdapter; 27 | import org.aarboard.nextcloud.api.utils.LocalDateXmlAdapter; 28 | 29 | /** 30 | * 31 | * @author a.schild 32 | */ 33 | @XmlAccessorType(XmlAccessType.FIELD) 34 | public class Share 35 | { 36 | private int id; 37 | @XmlElement(name = "share_type") 38 | private ShareType shareType; 39 | @XmlElement(name = "uid_owner") 40 | private String ownerId; 41 | @XmlElement(name = "displayname_owner") 42 | private String ownerDisplayName; 43 | @XmlElement(name = "permissions") 44 | @XmlJavaTypeAdapter(SharePermissionsAdapter.class) 45 | private SharePermissions sharePermissions; 46 | @XmlElement(name = "uid_file_owner") 47 | private String fileOwnerId; 48 | @XmlElement(name = "displayname_file_owner") 49 | private String fileOwnerDisplayName; 50 | private String path; 51 | @XmlElement(name = "item_type") 52 | private ItemType itemType; 53 | @XmlElement(name = "file_target") 54 | private String fileTarget; 55 | @XmlElement(name = "share_with") 56 | private String shareWithId; 57 | @XmlElement(name = "share_with_displayname") 58 | private String shareWithDisplayName; 59 | private String token; 60 | @XmlElement(name = "stime") 61 | @XmlJavaTypeAdapter(InstantXmlAdapter.class) 62 | private Instant shareTime; 63 | @XmlJavaTypeAdapter(LocalDateXmlAdapter.class) 64 | private LocalDate expiration; 65 | private String url; 66 | private String mimetype; 67 | 68 | public int getId() { 69 | return id; 70 | } 71 | 72 | public ShareType getShareType() { 73 | return shareType; 74 | } 75 | 76 | public String getOwnerId() { 77 | return ownerId; 78 | } 79 | 80 | public String getOwnerDisplayName() { 81 | return ownerDisplayName; 82 | } 83 | 84 | public SharePermissions getSharePermissions() { 85 | return sharePermissions; 86 | } 87 | 88 | public String getFileOwnerId() { 89 | return fileOwnerId; 90 | } 91 | 92 | public String getFileOwnerDisplayName() { 93 | return fileOwnerDisplayName; 94 | } 95 | 96 | public String getPath() { 97 | return path; 98 | } 99 | 100 | public ItemType getItemType() { 101 | return itemType; 102 | } 103 | 104 | public String getFileTarget() { 105 | return fileTarget; 106 | } 107 | 108 | public String getShareWithId() { 109 | return shareWithId; 110 | } 111 | 112 | public String getShareWithDisplayName() { 113 | return shareWithDisplayName; 114 | } 115 | 116 | public String getToken() { 117 | return token; 118 | } 119 | 120 | public Instant getShareTime() { 121 | return shareTime; 122 | } 123 | 124 | public LocalDate getExpiration() { 125 | return expiration; 126 | } 127 | 128 | public String getUrl() { 129 | return url; 130 | } 131 | 132 | public String getMimetype() { 133 | return mimetype; 134 | } 135 | 136 | private static final class SharePermissionsAdapter extends XmlAdapter 137 | { 138 | @Override 139 | public Integer marshal(SharePermissions sharePermissions) 140 | { 141 | return sharePermissions.getCurrentPermission(); 142 | } 143 | 144 | @Override 145 | public SharePermissions unmarshal(Integer v) 146 | { 147 | return new SharePermissions(v); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/SharePermissions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | /** 20 | * ... 21 | * @author a.schild 22 | */ 23 | public class SharePermissions { 24 | 25 | public enum SingleRight { 26 | READ(1), 27 | UPDATE(2), 28 | CREATE(4), 29 | DELETE(8), 30 | SHARE(16); 31 | 32 | private final int intValue; 33 | 34 | private SingleRight(int iV) { 35 | intValue= iV; 36 | } 37 | 38 | public int getIntValue() { 39 | return intValue; 40 | } 41 | } 42 | 43 | private final int currentPermission; 44 | 45 | public SharePermissions(int currentPermission) { 46 | this.currentPermission = currentPermission; 47 | } 48 | 49 | public SharePermissions(SingleRight... permissions) { 50 | int calculatedPermission = 0; 51 | for(SingleRight permission: permissions) 52 | { 53 | calculatedPermission += permission.getIntValue(); 54 | } 55 | this.currentPermission = calculatedPermission; 56 | } 57 | 58 | public boolean hasAllRights() 59 | { 60 | return currentPermission == ( 61 | SingleRight.READ.getIntValue()+ 62 | SingleRight.UPDATE.getIntValue()+ 63 | SingleRight.CREATE.getIntValue()+ 64 | SingleRight.DELETE.getIntValue()+ 65 | SingleRight.SHARE.getIntValue() 66 | ); 67 | } 68 | 69 | public boolean hasRight(SingleRight permission) { 70 | return (currentPermission & permission.intValue) != 0; 71 | } 72 | 73 | public int getCurrentPermission() { 74 | return currentPermission; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return Integer.toString(currentPermission); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/ShareType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import jakarta.xml.bind.annotation.XmlEnum; 20 | import jakarta.xml.bind.annotation.XmlEnumValue; 21 | import jakarta.xml.bind.annotation.XmlType; 22 | import java.security.InvalidParameterException; 23 | 24 | /** 25 | * ... 26 | * 27 | * @author a.schild 28 | */ 29 | @XmlType 30 | @XmlEnum(Integer.class) 31 | public enum ShareType { 32 | 33 | @XmlEnumValue("0") USER(0), 34 | @XmlEnumValue("1") GROUP(1), 35 | @XmlEnumValue("3") PUBLIC_LINK(3), 36 | @XmlEnumValue("4") EMAIL(4), 37 | @XmlEnumValue("6") FEDERATED_CLOUD_SHARE(6); 38 | 39 | private final int intValue; 40 | 41 | private ShareType(int iV) { 42 | intValue= iV; 43 | } 44 | 45 | public int getIntValue() { 46 | return intValue; 47 | } 48 | 49 | public static ShareType getShareTypeForIntValue(int i) 50 | { 51 | for (ShareType s : ShareType.values()) 52 | { 53 | if (s.getIntValue() == i) 54 | { 55 | return s; 56 | } 57 | } 58 | throw new InvalidParameterException("Invalid ShareType found " + i); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/SharesXMLAnswer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import jakarta.xml.bind.annotation.XmlElement; 20 | import jakarta.xml.bind.annotation.XmlElementWrapper; 21 | import jakarta.xml.bind.annotation.XmlRootElement; 22 | import java.util.List; 23 | import org.aarboard.nextcloud.api.utils.XMLAnswer; 24 | 25 | /** 26 | * 27 | * @author a.schild 28 | */ 29 | @XmlRootElement(name = "ocs") 30 | public class SharesXMLAnswer extends XMLAnswer 31 | { 32 | @XmlElementWrapper(name = "data") 33 | @XmlElement(name = "element") 34 | private List shares; 35 | 36 | public List getShares() { 37 | return shares; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/filesharing/SingleShareXMLAnswer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import jakarta.xml.bind.annotation.XmlElement; 20 | import jakarta.xml.bind.annotation.XmlRootElement; 21 | import org.aarboard.nextcloud.api.utils.XMLAnswer; 22 | 23 | /** 24 | * 25 | * @author a.schild 26 | */ 27 | @XmlRootElement(name = "ocs") 28 | public class SingleShareXMLAnswer extends XMLAnswer 29 | { 30 | @XmlElement(name = "data") 31 | private Share share= null; 32 | 33 | public Share getShare() { 34 | return share; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/GroupListAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import org.aarboard.nextcloud.api.utils.JsonAnswer; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | public class GroupListAnswer extends JsonAnswer { 11 | @JsonProperty 12 | private Data data; 13 | 14 | @JsonIgnore 15 | public List getAllGroups() { 16 | if (data != null && data.groups != null) { 17 | return data.groups; 18 | } 19 | return Collections.emptyList(); 20 | } 21 | 22 | public static class Data { 23 | @JsonProperty 24 | private List groups; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/Quota.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | 6 | import java.util.Optional; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class Quota { 10 | @JsonDeserialize(using = QuotaDeserializer.class) 11 | private Optional quota = Optional.empty(); 12 | private long free; 13 | private long used; 14 | private long total; 15 | private float relative; 16 | 17 | public Optional getQuota() { 18 | return quota; 19 | } 20 | 21 | public long getFree() { 22 | return free; 23 | } 24 | 25 | public long getUsed() { 26 | return used; 27 | } 28 | 29 | public long getTotal() { 30 | return total; 31 | } 32 | 33 | public float getRelative() { 34 | return relative; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/QuotaDeserializer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonDeserializer; 7 | import org.apache.commons.lang3.math.NumberUtils; 8 | 9 | import java.io.IOException; 10 | import java.util.Optional; 11 | 12 | public class QuotaDeserializer extends JsonDeserializer> { 13 | 14 | @Override 15 | public Optional deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { 16 | Long result = null; 17 | String quota = jsonParser.getValueAsString(); 18 | if (NumberUtils.isCreatable(quota)) { 19 | result = NumberUtils.createLong(quota); 20 | } 21 | 22 | return Optional.ofNullable(result); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/ShareData.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | public enum ShareData 4 | { 5 | PERMISSIONS("permissions"), 6 | PASSWORD("password"), 7 | PUBLICUPLOAD("publicUpload"), 8 | EXPIREDATE("expireDate"); 9 | 10 | public final String parameterName; 11 | 12 | ShareData(String parameterName) 13 | { 14 | this.parameterName = parameterName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.provisioning; 18 | 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 20 | 21 | import java.util.List; 22 | 23 | /** 24 | * 25 | * @author a.schild 26 | */ 27 | @JsonIgnoreProperties(ignoreUnknown = true) 28 | public class User 29 | { 30 | private String id; 31 | private boolean enabled; 32 | private String email; 33 | private String displayname; 34 | private String phone; 35 | private String address; 36 | private String website; 37 | private String twitter; 38 | private Quota quota; 39 | private List groups; 40 | 41 | public String getId() { 42 | return id; 43 | } 44 | 45 | public boolean isEnabled() { 46 | return enabled; 47 | } 48 | 49 | public String getEmail() { 50 | return email; 51 | } 52 | 53 | public String getDisplayname() { 54 | return displayname; 55 | } 56 | 57 | public String getPhone() { 58 | return phone; 59 | } 60 | 61 | public String getAddress() { 62 | return address; 63 | } 64 | 65 | public String getWebsite() { 66 | return website; 67 | } 68 | 69 | public String getTwitter() { 70 | return twitter; 71 | } 72 | 73 | public Quota getQuota() { 74 | return quota; 75 | } 76 | 77 | public List getGroups() { 78 | return groups; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/UserData.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | public enum UserData { 4 | EMAIL, 5 | QUOTA, 6 | DISPLAYNAME, 7 | PHONE, 8 | ADDRESS, 9 | WEBSITE, 10 | TWITTER, 11 | PASSWORD 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/UserDetailsAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonRootName; 6 | import org.aarboard.nextcloud.api.utils.JsonAnswer; 7 | 8 | @JsonRootName(value = "ocs") 9 | public class UserDetailsAnswer extends JsonAnswer { 10 | @JsonProperty 11 | private User data; 12 | 13 | @JsonIgnore 14 | public User getUserDetails() { 15 | if (data != null ) { 16 | return data; 17 | } 18 | return null; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/UserDetailsListAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonRootName; 6 | import java.util.ArrayList; 7 | import org.aarboard.nextcloud.api.utils.JsonAnswer; 8 | 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | @JsonRootName(value = "ocs") 15 | public class UserDetailsListAnswer extends JsonAnswer { 16 | @JsonProperty 17 | private Data data; 18 | 19 | @JsonIgnore 20 | public List getAllUserDetails() { 21 | if (data != null && data.users != null) { 22 | return new ArrayList<>(data.users.values()); 23 | } 24 | return Collections.emptyList(); 25 | } 26 | 27 | public static class Data { 28 | @JsonProperty 29 | private Users users; 30 | } 31 | 32 | public static class Users extends HashMap {} 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/provisioning/UserListAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.provisioning; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import org.aarboard.nextcloud.api.utils.JsonAnswer; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | public class UserListAnswer extends JsonAnswer { 11 | @JsonProperty 12 | private Data data; 13 | 14 | @JsonIgnore 15 | public List getAllUsers() { 16 | if (data != null && data.users != null) { 17 | return data.users; 18 | } 19 | return Collections.emptyList(); 20 | } 21 | 22 | public static class Data { 23 | @JsonProperty 24 | private List users; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/ConnectorCommon.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStreamReader; 5 | import java.io.Reader; 6 | import java.net.URI; 7 | import java.net.URISyntaxException; 8 | import java.nio.charset.Charset; 9 | import java.security.KeyManagementException; 10 | import java.security.KeyStoreException; 11 | import java.security.NoSuchAlgorithmException; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.CompletableFuture; 15 | 16 | import javax.net.ssl.SSLContext; 17 | 18 | import org.aarboard.nextcloud.api.ServerConfig; 19 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 20 | import org.apache.http.HttpEntity; 21 | import org.apache.http.HttpHost; 22 | import org.apache.http.HttpResponse; 23 | import org.apache.http.HttpStatus; 24 | import org.apache.http.HttpVersion; 25 | import org.apache.http.NameValuePair; 26 | import org.apache.http.StatusLine; 27 | import org.apache.http.auth.AuthScope; 28 | import org.apache.http.auth.UsernamePasswordCredentials; 29 | import org.apache.http.client.AuthCache; 30 | import org.apache.http.client.ClientProtocolException; 31 | import org.apache.http.client.CredentialsProvider; 32 | import org.apache.http.client.methods.HttpDelete; 33 | import org.apache.http.client.methods.HttpGet; 34 | import org.apache.http.client.methods.HttpPost; 35 | import org.apache.http.client.methods.HttpPut; 36 | import org.apache.http.client.methods.HttpRequestBase; 37 | import org.apache.http.client.protocol.HttpClientContext; 38 | import org.apache.http.client.utils.URIBuilder; 39 | import org.apache.http.concurrent.FutureCallback; 40 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 41 | import org.apache.http.conn.ssl.TrustAllStrategy; 42 | import org.apache.http.entity.ContentType; 43 | import org.apache.http.impl.auth.BasicScheme; 44 | import org.apache.http.impl.client.BasicAuthCache; 45 | import org.apache.http.impl.client.BasicCredentialsProvider; 46 | import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; 47 | import org.apache.http.impl.nio.client.HttpAsyncClients; 48 | import org.apache.http.message.BasicNameValuePair; 49 | import org.apache.http.ssl.SSLContexts; 50 | 51 | public class ConnectorCommon 52 | { 53 | private final ServerConfig serverConfig; 54 | 55 | public ConnectorCommon(ServerConfig serverConfig) { 56 | this.serverConfig = serverConfig; 57 | } 58 | 59 | public CompletableFuture executeGet(String part, ResponseParser parser) { 60 | return executeGet(part, null, parser); 61 | } 62 | 63 | public CompletableFuture executeGet(String part, List queryParams, ResponseParser parser) { 64 | try { 65 | URI url= buildUrl(part, queryParams, parser instanceof JsonAnswerParser); 66 | 67 | HttpRequestBase request = new HttpGet(url.toString()); 68 | return executeRequest(parser, request); 69 | } catch (IOException e) { 70 | throw new NextcloudApiException(e); 71 | } 72 | } 73 | 74 | public CompletableFuture executePost(String part, ResponseParser parser) { 75 | return executePost(part, null, parser); 76 | } 77 | 78 | public CompletableFuture executePost(String part, List postParams, ResponseParser parser) { 79 | try { 80 | URI url= buildUrl(part, postParams, parser instanceof JsonAnswerParser); 81 | 82 | HttpRequestBase request = new HttpPost(url.toString()); 83 | return executeRequest(parser, request); 84 | } catch (IOException e) { 85 | throw new NextcloudApiException(e); 86 | } 87 | } 88 | 89 | public CompletableFuture executePut(String part1, String part2, ResponseParser parser) { 90 | return executePut(part1, part2, null, parser); 91 | } 92 | 93 | public CompletableFuture executePut(String part1, String part2, List putParams, ResponseParser parser) { 94 | try { 95 | URI url= buildUrl(part1 + "/" + part2, putParams, parser instanceof JsonAnswerParser); 96 | 97 | HttpRequestBase request = new HttpPut(url.toString()); 98 | return executeRequest(parser, request); 99 | } catch (IOException e) { 100 | throw new NextcloudApiException(e); 101 | } 102 | } 103 | 104 | public CompletableFuture executeDelete(String part1, String part2, ResponseParser parser) { 105 | return executeDelete(part1, part2, null, parser); 106 | } 107 | 108 | public CompletableFuture executeDelete(String part1, String part2, List deleteParams, ResponseParser parser) { 109 | try { 110 | URI url= buildUrl(part1 + "/" + part2, deleteParams, parser instanceof JsonAnswerParser); 111 | 112 | HttpRequestBase request = new HttpDelete(url.toString()); 113 | return executeRequest(parser, request); 114 | } catch (IOException e) { 115 | throw new NextcloudApiException(e); 116 | } 117 | } 118 | 119 | private URI buildUrl(String subPath, List queryParams, boolean useJson) { 120 | if(serverConfig.getSubPathPrefix()!=null) { 121 | subPath = serverConfig.getSubPathPrefix()+"/"+subPath; 122 | } 123 | 124 | if (useJson) { 125 | if (queryParams == null || queryParams.isEmpty()) { 126 | queryParams = new ArrayList<>(); 127 | } 128 | queryParams.add(new BasicNameValuePair("format", "json")); 129 | } 130 | 131 | URIBuilder uB= new URIBuilder() 132 | .setScheme(serverConfig.isUseHTTPS() ? "https" : "http") 133 | .setHost(serverConfig.getServerName()) 134 | .setPort(serverConfig.getPort()) 135 | .setPath(subPath); 136 | 137 | if (serverConfig.getAuthenticationConfig().usesBasicAuthentication()) { 138 | uB.setUserInfo(serverConfig.getAuthenticationConfig().getLoginName(), 139 | serverConfig.getAuthenticationConfig().getPassword()); 140 | } 141 | 142 | if (queryParams != null) { 143 | uB.addParameters(queryParams); 144 | } 145 | 146 | try { 147 | return uB.build(); 148 | } catch (URISyntaxException e) { 149 | throw new NextcloudApiException(e); 150 | } 151 | } 152 | 153 | private CompletableFuture executeRequest(final ResponseParser parser, HttpRequestBase request) throws IOException { 154 | // https://docs.nextcloud.com/server/14/developer_manual/core/ocs-share-api.html 155 | request.addHeader("OCS-APIRequest", "true"); 156 | request.addHeader("Content-Type", "application/x-www-form-urlencoded"); 157 | if (serverConfig.getAuthenticationConfig().usesBearerTokenAuthentication()) { 158 | request.addHeader("Authorization", "Bearer " + serverConfig.getAuthenticationConfig().getBearerToken()); 159 | } 160 | request.setProtocolVersion(HttpVersion.HTTP_1_1); 161 | 162 | HttpClientContext context = prepareContext(); 163 | 164 | CompletableFuture futureResponse = new CompletableFuture<>(); 165 | HttpAsyncClientSingleton.getInstance(serverConfig).execute(request, context, new ResponseCallback<>(parser, futureResponse)); 166 | return futureResponse; 167 | } 168 | 169 | private HttpClientContext prepareContext() { 170 | if (serverConfig.getAuthenticationConfig().usesBasicAuthentication()) { 171 | HttpHost targetHost = new HttpHost(serverConfig.getServerName(), serverConfig.getPort(), serverConfig.isUseHTTPS() ? "https" : "http"); 172 | AuthCache authCache = new BasicAuthCache(); 173 | authCache.put(targetHost, new BasicScheme()); 174 | 175 | CredentialsProvider credsProvider = new BasicCredentialsProvider(); 176 | UsernamePasswordCredentials credentials 177 | = new UsernamePasswordCredentials(serverConfig.getAuthenticationConfig().getLoginName(), serverConfig.getAuthenticationConfig().getPassword()); 178 | credsProvider.setCredentials(AuthScope.ANY, credentials); 179 | 180 | // Add AuthCache to the execution context 181 | HttpClientContext context = HttpClientContext.create(); 182 | context.setCredentialsProvider(credsProvider); 183 | context.setAuthCache(authCache); 184 | return context; 185 | } 186 | return HttpClientContext.create(); 187 | } 188 | 189 | private static final class ResponseCallback implements FutureCallback { 190 | private final ResponseParser parser; 191 | private final CompletableFuture futureResponse; 192 | 193 | private ResponseCallback(ResponseParser parser, CompletableFuture futureResponse) { 194 | this.parser = parser; 195 | this.futureResponse = futureResponse; 196 | } 197 | 198 | @Override 199 | public void completed(HttpResponse response) { 200 | try { 201 | R result = handleResponse(parser, response); 202 | futureResponse.complete(result); 203 | } catch(Exception ex) { 204 | futureResponse.completeExceptionally(ex); 205 | } 206 | } 207 | 208 | private R handleResponse(ResponseParser parser, HttpResponse response) throws IOException { 209 | StatusLine statusLine= response.getStatusLine(); 210 | if (statusLine.getStatusCode() == HttpStatus.SC_OK) { 211 | HttpEntity entity = response.getEntity(); 212 | if (entity != null) { 213 | Charset charset = ContentType.getOrDefault(entity).getCharset(); 214 | Reader reader = new InputStreamReader(entity.getContent(), charset); 215 | return parser.parseResponse(reader); 216 | } 217 | throw new NextcloudApiException("Empty response received"); 218 | } 219 | throw new NextcloudApiException(String.format("Request failed with %d %s", statusLine.getStatusCode(), statusLine.getReasonPhrase())); 220 | } 221 | 222 | @Override 223 | public void failed(Exception ex) { 224 | futureResponse.completeExceptionally(ex); 225 | } 226 | 227 | @Override 228 | public void cancelled() { 229 | futureResponse.cancel(true); 230 | } 231 | } 232 | 233 | private static class HttpAsyncClientSingleton { 234 | private static CloseableHttpAsyncClient HTTPC_CLIENT; 235 | 236 | private HttpAsyncClientSingleton(){} 237 | 238 | public static CloseableHttpAsyncClient getInstance(ServerConfig serverConfig) 239 | throws IOException{ 240 | if (HTTPC_CLIENT == null) { 241 | if (serverConfig.isTrustAllCertificates()) { 242 | try { 243 | SSLContext sslContext = SSLContexts.custom() 244 | .loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build(); 245 | HTTPC_CLIENT = HttpAsyncClients.custom() 246 | .setSSLHostnameVerifier((NoopHostnameVerifier.INSTANCE)) 247 | .setSSLContext(sslContext) 248 | .build(); 249 | } catch (KeyManagementException | NoSuchAlgorithmException 250 | | KeyStoreException e) { 251 | throw new IOException(e); 252 | } 253 | 254 | } else { 255 | HTTPC_CLIENT = HttpAsyncClients.createDefault(); 256 | } 257 | 258 | HTTPC_CLIENT.start(); 259 | } 260 | return HTTPC_CLIENT; 261 | } 262 | 263 | } 264 | 265 | public interface ResponseParser { 266 | R parseResponse(Reader reader); 267 | } 268 | 269 | /** 270 | * Close the http client. Required for clean shutdown. 271 | * @throws IOException error on shutdown 272 | */ 273 | public static void shutdown() throws IOException { 274 | if(HttpAsyncClientSingleton.HTTPC_CLIENT != null) { 275 | HttpAsyncClientSingleton.getInstance(null).close(); 276 | HttpAsyncClientSingleton.HTTPC_CLIENT = null; 277 | } 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/InstantXmlAdapter.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import jakarta.xml.bind.annotation.adapters.XmlAdapter; 4 | import java.time.Instant; 5 | 6 | 7 | public class InstantXmlAdapter extends XmlAdapter 8 | { 9 | @Override 10 | public Long marshal(Instant instant) 11 | { 12 | return instant.getEpochSecond(); 13 | } 14 | 15 | @Override 16 | public Instant unmarshal(Long time) 17 | { 18 | return Instant.ofEpochSecond(time); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/JsonAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.annotation.JsonRootName; 5 | 6 | @JsonRootName(value = "ocs") 7 | public class JsonAnswer implements NextcloudResponse { 8 | @JsonProperty 9 | private Meta meta; 10 | 11 | @Override 12 | public String getStatus() { 13 | return meta.status; 14 | } 15 | 16 | @Override 17 | public int getStatusCode() { 18 | return meta.statusCode; 19 | } 20 | 21 | @Override 22 | public String getMessage() { 23 | return meta.message; 24 | } 25 | 26 | @Override 27 | public int getTotalItems() { 28 | return meta.totalItems; 29 | } 30 | 31 | @Override 32 | public int getItemsPerPage() { 33 | return meta.itemsPerPage; 34 | } 35 | 36 | public static class Meta { 37 | @JsonProperty 38 | public String status = null; 39 | @JsonProperty(value = "statuscode") 40 | public int statusCode = -1; 41 | @JsonProperty 42 | public String message = null; 43 | @JsonProperty(value = "totalitems") 44 | public int totalItems = -1; 45 | @JsonProperty(value = "itemsperpage") 46 | public int itemsPerPage = -1; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/JsonAnswerParser.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.ObjectReader; 6 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 7 | 8 | import java.io.IOException; 9 | import java.io.Reader; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public class JsonAnswerParser implements ConnectorCommon.ResponseParser { 14 | 15 | private static final Map> PARSERS = new HashMap<>(); 16 | 17 | private final ObjectReader objectReader; 18 | 19 | private JsonAnswerParser(Class answerClass) { 20 | ObjectMapper objectMapper = new ObjectMapper(); 21 | objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); 22 | 23 | objectReader = objectMapper.readerFor(answerClass); 24 | } 25 | 26 | public static JsonAnswerParser getInstance(Class answerClass) { 27 | @SuppressWarnings("unchecked") 28 | JsonAnswerParser parser = (JsonAnswerParser) PARSERS.get(answerClass.getName()); 29 | if (parser == null) { 30 | synchronized (PARSERS) { 31 | parser = new JsonAnswerParser<>(answerClass); 32 | PARSERS.put(answerClass.getName(), parser); 33 | } 34 | } 35 | return parser; 36 | } 37 | 38 | @Override 39 | public A parseResponse(Reader reader) { 40 | try (Reader response = reader) { 41 | return objectReader.readValue(response); 42 | } catch (IOException e) { 43 | throw new NextcloudApiException(e); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/JsonListAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonRootName; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | @JsonRootName(value = "ocs") 11 | public class JsonListAnswer extends JsonAnswer { 12 | @JsonProperty 13 | private List data; 14 | 15 | @JsonIgnore 16 | public List getResult() { 17 | if (data != null ) { 18 | return data; 19 | } 20 | return Collections.emptyList(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/JsonVoidAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonRootName; 5 | 6 | @JsonRootName(value = "ocs") 7 | @JsonIgnoreProperties({"data"}) 8 | public class JsonVoidAnswer extends JsonAnswer { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/ListXMLAnswer.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import jakarta.xml.bind.annotation.XmlElement; 4 | import jakarta.xml.bind.annotation.XmlElementWrapper; 5 | import jakarta.xml.bind.annotation.XmlRootElement; 6 | import java.util.List; 7 | 8 | 9 | 10 | @XmlRootElement(name = "ocs") 11 | public class ListXMLAnswer extends XMLAnswer 12 | { 13 | @XmlElementWrapper(name = "data") 14 | @XmlElement(name = "element") 15 | private List result; 16 | 17 | public List getResult() { 18 | return result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/LocalDateXmlAdapter.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import jakarta.xml.bind.annotation.adapters.XmlAdapter; 4 | import java.time.LocalDate; 5 | 6 | 7 | public class LocalDateXmlAdapter extends XmlAdapter 8 | { 9 | @Override 10 | public String marshal(LocalDate date) 11 | { 12 | return date.toString(); 13 | } 14 | 15 | @Override 16 | public LocalDate unmarshal(String date) 17 | { 18 | return LocalDate.parse(date.substring(0, 10)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/NextcloudResponse.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | public interface NextcloudResponse { 4 | 5 | /** 6 | * @return the status 7 | */ 8 | String getStatus(); 9 | 10 | /** 11 | * @return the statusCode 12 | */ 13 | int getStatusCode(); 14 | 15 | /** 16 | * @return the message 17 | */ 18 | String getMessage(); 19 | 20 | /** 21 | * @return the totalItems 22 | */ 23 | int getTotalItems(); 24 | 25 | /** 26 | * @return the itemsPerPage 27 | */ 28 | int getItemsPerPage(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/NextcloudResponseHelper.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 6 | import org.aarboard.nextcloud.api.exception.NextcloudOperationFailedException; 7 | 8 | public class NextcloudResponseHelper 9 | { 10 | public static final int NC_OK= 100; // Nextcloud OK message 11 | 12 | private NextcloudResponseHelper() { 13 | } 14 | 15 | public static A getAndCheckStatus(CompletableFuture answer) 16 | { 17 | A wrappedAnswer = getAndWrapException(answer); 18 | if(isStatusCodeOkay(wrappedAnswer)) 19 | { 20 | return wrappedAnswer; 21 | } 22 | throw new NextcloudOperationFailedException(wrappedAnswer.getStatusCode(), wrappedAnswer.getMessage()); 23 | } 24 | 25 | public static boolean isStatusCodeOkay(CompletableFuture answer) 26 | { 27 | return isStatusCodeOkay(getAndWrapException(answer)); 28 | } 29 | 30 | public static boolean isStatusCodeOkay(NextcloudResponse answer) 31 | { 32 | return answer.getStatusCode() == NC_OK; 33 | } 34 | 35 | public static A getAndWrapException(CompletableFuture answer) 36 | { 37 | try { 38 | return answer.get(); 39 | } catch (Exception e) { 40 | throw new NextcloudApiException(e); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/NextcloudSearch.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import org.apache.http.NameValuePair; 4 | import org.apache.http.message.BasicNameValuePair; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | public class NextcloudSearch { 10 | private final String search; 11 | private final int limit; 12 | private final int offset; 13 | 14 | public NextcloudSearch(String search, int limit, int offset) { 15 | this.search = search; 16 | this.limit = limit; 17 | this.offset = offset; 18 | } 19 | 20 | public List asQueryParameters() { 21 | List result = new LinkedList<>(); 22 | 23 | if (limit != -1) { 24 | result.add(new BasicNameValuePair("limit", Integer.toString(limit))); 25 | } 26 | 27 | if (offset != -1) { 28 | result.add(new BasicNameValuePair("offset", Integer.toString(offset))); 29 | } 30 | 31 | if (search != null) { 32 | result.add(new BasicNameValuePair("search", search)); 33 | } 34 | 35 | return result; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/WebdavInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 andre 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.utils; 18 | 19 | import com.github.sardine.Sardine; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import org.apache.commons.io.input.ProxyInputStream; 23 | 24 | /** 25 | * 26 | * @author andre 27 | */ 28 | public class WebdavInputStream extends ProxyInputStream { 29 | 30 | private final Sardine sardine; // Sardine instance used 31 | 32 | public WebdavInputStream(Sardine sardine, InputStream in) 33 | { 34 | super(in); 35 | this.sardine= sardine; 36 | } 37 | 38 | @Override 39 | public void close() throws IOException { 40 | super.close(); 41 | sardine.shutdown(); 42 | } 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/XMLAnswer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.utils; 18 | 19 | import jakarta.xml.bind.annotation.XmlAccessType; 20 | import jakarta.xml.bind.annotation.XmlAccessorType; 21 | import jakarta.xml.bind.annotation.XmlElement; 22 | import jakarta.xml.bind.annotation.XmlRootElement; 23 | 24 | 25 | /** 26 | * 27 | * @author a.schild 28 | */ 29 | @XmlRootElement(name="ocs") 30 | @XmlAccessorType(XmlAccessType.FIELD) 31 | public class XMLAnswer implements NextcloudResponse { 32 | private Meta meta; 33 | 34 | @Override 35 | public String getStatus() { 36 | return meta.status; 37 | } 38 | 39 | @Override 40 | public int getStatusCode() { 41 | return meta.statusCode; 42 | } 43 | 44 | @Override 45 | public String getMessage() { 46 | return meta.message; 47 | } 48 | 49 | @Override 50 | public int getTotalItems() { 51 | return meta.totalItems; 52 | } 53 | 54 | @Override 55 | public int getItemsPerPage() { 56 | return meta.itemsPerPage; 57 | } 58 | 59 | @XmlAccessorType(XmlAccessType.FIELD) 60 | private static final class Meta { 61 | private String status= null; 62 | @XmlElement(name="statuscode") 63 | private int statusCode= -1; 64 | private String message= null; 65 | @XmlElement(name="totalitems") 66 | private int totalItems= -1; 67 | @XmlElement(name="itemsperpage") 68 | private int itemsPerPage= -1; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/utils/XMLAnswerParser.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.utils; 2 | 3 | import jakarta.xml.bind.JAXBContext; 4 | import jakarta.xml.bind.JAXBException; 5 | import jakarta.xml.bind.Unmarshaller; 6 | import java.io.IOException; 7 | import java.io.Reader; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 11 | import org.aarboard.nextcloud.api.utils.ConnectorCommon.ResponseParser; 12 | 13 | public class XMLAnswerParser implements ResponseParser 14 | { 15 | private static final Map> PARSERS = new HashMap<>(); 16 | 17 | private final JAXBContext jAXBContext; 18 | 19 | public XMLAnswerParser(Class answerClass) 20 | { 21 | try { 22 | jAXBContext = JAXBContext.newInstance(XMLAnswer.class, answerClass); 23 | } catch (JAXBException e) { 24 | throw new NextcloudApiException(e); 25 | } 26 | } 27 | 28 | public static XMLAnswerParser getInstance(Class answerClass) 29 | { 30 | @SuppressWarnings("unchecked") 31 | XMLAnswerParser parser = (XMLAnswerParser) PARSERS.get(answerClass.getName()); 32 | if (parser == null) 33 | { 34 | synchronized (PARSERS) 35 | { 36 | parser = new XMLAnswerParser<>(answerClass); 37 | PARSERS.put(answerClass.getName(), parser); 38 | } 39 | } 40 | return parser; 41 | } 42 | 43 | @Override 44 | public A parseResponse(Reader xmlStream) 45 | { 46 | try { 47 | return tryParseAnswer(xmlStream); 48 | } catch (Exception e) { 49 | throw new NextcloudApiException(e); 50 | } finally { 51 | try { 52 | xmlStream.close(); 53 | } catch (IOException e) { 54 | // Ignore 55 | } 56 | } 57 | } 58 | 59 | @SuppressWarnings("unchecked") 60 | private A tryParseAnswer(Reader xmlStream) throws JAXBException { 61 | Unmarshaller unmarshaller = jAXBContext.createUnmarshaller(); 62 | Object result = unmarshaller.unmarshal(xmlStream); 63 | return (A) result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/AWebdavHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav; 18 | 19 | import com.github.sardine.Sardine; 20 | import com.github.sardine.SardineFactory; 21 | import java.io.BufferedReader; 22 | import com.github.sardine.impl.SardineImpl; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.InputStreamReader; 26 | import java.nio.charset.StandardCharsets; 27 | import java.util.Arrays; 28 | import java.util.stream.Collectors; 29 | import org.aarboard.nextcloud.api.ServerConfig; 30 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 31 | import org.aarboard.nextcloud.api.provisioning.ProvisionConnector; 32 | import org.aarboard.nextcloud.api.provisioning.User; 33 | import org.aarboard.nextcloud.api.webdav.pathresolver.NextcloudVersion; 34 | import org.aarboard.nextcloud.api.webdav.pathresolver.WebDavPathResolver; 35 | import org.aarboard.nextcloud.api.webdav.pathresolver.WebDavPathResolverBuilder; 36 | import org.apache.http.client.utils.URIBuilder; 37 | import org.slf4j.Logger; 38 | import org.slf4j.LoggerFactory; 39 | 40 | /** 41 | * 42 | * @author a.schild 43 | */ 44 | public abstract class AWebdavHandler 45 | { 46 | 47 | private static final Logger LOG = LoggerFactory.getLogger(AWebdavHandler.class); 48 | private static final String ERROR_CLOSING = "error in closing sardine connector"; 49 | 50 | public static final int FILE_BUFFER_SIZE= 4096; 51 | public static final String WEB_DAV_BASE_PATH = "remote.php/webdav/"; 52 | 53 | private final ServerConfig serverConfig; 54 | 55 | private WebDavPathResolver resolver; 56 | 57 | private String nextcloudServerVersion; 58 | 59 | protected AWebdavHandler(ServerConfig serverConfig) 60 | { 61 | this.serverConfig = serverConfig; 62 | } 63 | 64 | public void setWebDavPathResolver(final WebDavPathResolver resolver) 65 | { 66 | this.resolver = resolver; 67 | } 68 | 69 | /** 70 | * @return the nextcloud server instance version 71 | */ 72 | public String getServerVersion() 73 | { 74 | if (null == nextcloudServerVersion) 75 | { 76 | resolveNextcloudServerVersion(); 77 | } 78 | 79 | return nextcloudServerVersion; 80 | } 81 | 82 | private void resolveNextcloudServerVersion() 83 | { 84 | final WebDavPathResolver versionResolver = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.VERSION).withBasePathPrefix(this.serverConfig.getSubPathPrefix()).build(); 85 | 86 | final String url = buildWebdavPath(versionResolver, ""); 87 | final Sardine sardine = buildAuthSardine(); 88 | 89 | try (final InputStream inputStream = sardine.get(url)) 90 | { 91 | final String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) 92 | .lines().collect(Collectors.joining("\n")); 93 | 94 | //TODO parse with proper json api 95 | nextcloudServerVersion = Arrays.stream(json.split(",")).filter(x -> x.contains("version")).map(x -> x.split(":")[1]).findAny().orElse("20.0").replace("\"", ""); 96 | 97 | } 98 | catch (IOException ex) 99 | { 100 | throw new NextcloudApiException(ex); 101 | } 102 | finally 103 | { 104 | try 105 | { 106 | sardine.shutdown(); 107 | } 108 | catch (IOException ex) 109 | { 110 | LOG.warn(ERROR_CLOSING, ex); 111 | } 112 | } 113 | 114 | } 115 | 116 | /** 117 | * Defaults to FILES resolver 118 | * 119 | * @return the resolver 120 | * @since 11.5 121 | */ 122 | protected WebDavPathResolver getWebDavPathResolver() 123 | { 124 | if (null == this.resolver) 125 | { 126 | ProvisionConnector pc= new ProvisionConnector(this.serverConfig); 127 | User currentUser= pc.getCurrentUser(); 128 | this.resolver = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.FILES)// 129 | .ofVersion(NextcloudVersion.get(getServerVersion())) 130 | .withUserName(currentUser.getId()) 131 | .withBasePathSuffix("files") 132 | .withBasePathPrefix(this.serverConfig.getSubPathPrefix()).build(); 133 | } 134 | 135 | return this.resolver; 136 | } 137 | 138 | /** 139 | * Build the full URL for the webdav access to a resource 140 | * 141 | * @param remotePath remote path for file (Not including remote.php/webdav/) 142 | * @return Full URL including http.... 143 | */ 144 | protected String buildWebdavPath(String remotePath) 145 | { 146 | return buildWebdavPath(getWebDavPathResolver(), remotePath); 147 | } 148 | 149 | protected String buildWebdavPath(WebDavPathResolver resolver, String remotePath) 150 | { 151 | URIBuilder uB = new URIBuilder() 152 | .setScheme(this.serverConfig.isUseHTTPS() ? "https" : "http") 153 | .setHost(this.serverConfig.getServerName()) 154 | .setPort(this.serverConfig.getPort()) 155 | .setPath(resolver.getWebDavPath(remotePath)); 156 | return uB.toString(); 157 | } 158 | 159 | protected String getWebdavPathPrefix() 160 | { 161 | if (resolver != null) 162 | { 163 | return resolver.getWebDavPath(); 164 | } 165 | else 166 | { 167 | return "/"+WEB_DAV_BASE_PATH; 168 | } 169 | } 170 | 171 | /** 172 | * Create a authenticate sardine connector 173 | * 174 | * @return sardine connector to server including authentication 175 | */ 176 | protected Sardine buildAuthSardine() 177 | { 178 | if (this.serverConfig.getAuthenticationConfig().usesBasicAuthentication()) { 179 | Sardine sardine = SardineFactory.begin(); 180 | sardine.setCredentials(this.serverConfig.getUserName(), 181 | this.serverConfig.getAuthenticationConfig().getPassword()); 182 | sardine.enablePreemptiveAuthentication(this.serverConfig.getServerName()); 183 | return sardine; 184 | } 185 | return new SardineImpl(this.serverConfig.getAuthenticationConfig().getBearerToken()); 186 | } 187 | 188 | /** 189 | * method to check if a remote object already exists 190 | * 191 | * @param remotePath path of the file/folder 192 | * @return boolean value if the given file/folder exists or not 193 | */ 194 | public boolean pathExists(String remotePath) 195 | { 196 | String path = buildWebdavPath(remotePath); 197 | Sardine sardine = buildAuthSardine(); 198 | 199 | try 200 | { 201 | return sardine.exists(path); 202 | } 203 | catch (IOException e) 204 | { 205 | throw new NextcloudApiException(e); 206 | } 207 | finally 208 | { 209 | try 210 | { 211 | sardine.shutdown(); 212 | } 213 | catch (IOException ex) 214 | { 215 | LOG.warn(ERROR_CLOSING, ex); 216 | } 217 | } 218 | } 219 | 220 | /** 221 | * Deletes the file/folder at the specified path 222 | * 223 | * @param remotePath path of the file/folder 224 | */ 225 | public void deletePath(String remotePath) 226 | { 227 | String path = buildWebdavPath(remotePath); 228 | 229 | Sardine sardine = buildAuthSardine(); 230 | try 231 | { 232 | sardine.delete(path); 233 | } 234 | catch (IOException e) 235 | { 236 | throw new NextcloudApiException(e); 237 | } 238 | finally 239 | { 240 | try 241 | { 242 | sardine.shutdown(); 243 | } 244 | catch (IOException ex) 245 | { 246 | LOG.warn(ERROR_CLOSING, ex); 247 | } 248 | } 249 | } 250 | 251 | /** 252 | * Rename the file/folder at the specified path 253 | * 254 | * @param oldPath path of the original file/folder 255 | * @param newPath path of the new file/folder 256 | * @param overwriteExisting Should an existing target be overwritten? 257 | */ 258 | public void renamePath(String oldPath, String newPath, boolean overwriteExisting) 259 | { 260 | String oldWEBDavpath= buildWebdavPath( oldPath ); 261 | String newWEBDavpath= buildWebdavPath( newPath ); 262 | 263 | Sardine sardine = buildAuthSardine(); 264 | try { 265 | sardine.move(oldWEBDavpath, newWEBDavpath, overwriteExisting); 266 | } catch (IOException e) { 267 | throw new NextcloudApiException(e); 268 | } 269 | finally 270 | { 271 | try 272 | { 273 | sardine.shutdown(); 274 | } 275 | catch (IOException ex) 276 | { 277 | LOG.warn(ERROR_CLOSING, ex); 278 | } 279 | } 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/Files.java: -------------------------------------------------------------------------------- 1 | package org.aarboard.nextcloud.api.webdav; 2 | 3 | 4 | import com.github.sardine.DavResource; 5 | import com.github.sardine.Sardine; 6 | import java.io.File; 7 | import java.io.OutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import javax.xml.namespace.QName; 15 | import org.aarboard.nextcloud.api.utils.WebdavInputStream; 16 | import org.aarboard.nextcloud.api.ServerConfig; 17 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | /** 22 | * 23 | * @author tott 24 | * 25 | */ 26 | public class Files extends AWebdavHandler{ 27 | 28 | private static final Logger LOG = LoggerFactory.getLogger(Files.class); 29 | private static final String ERROR_SHUTDOWN = "Error in sardine shutdown"; 30 | public static final String URI_NS_OWNCLOUD = "http://owncloud.org/ns"; 31 | public static final String URI_NS_DAV = "DAV:"; 32 | 33 | public Files(ServerConfig serverConfig) { 34 | super(serverConfig); 35 | } 36 | 37 | /** 38 | * method to check if a file already exists 39 | * 40 | * @param remotePath path of the file 41 | * @return boolean value if the given file exists or not 42 | */ 43 | public boolean fileExists(String remotePath) { 44 | return pathExists(remotePath); 45 | } 46 | 47 | /** 48 | * Uploads a file at the specified path with the data from the File 49 | * 50 | * @param localSource file which should be uploaded 51 | * @param remotePath path where the file should be uploaded to 52 | */ 53 | public void uploadFile(File localSource, String remotePath) { 54 | String path = buildWebdavPath(remotePath); 55 | Sardine sardine = buildAuthSardine(); 56 | 57 | try 58 | { 59 | sardine.put(path, localSource, null, true); 60 | } catch (IOException e) 61 | { 62 | throw new NextcloudApiException(e); 63 | } 64 | finally 65 | { 66 | try 67 | { 68 | sardine.shutdown(); 69 | } 70 | catch(Exception ex2) 71 | { 72 | LOG.warn(ERROR_SHUTDOWN, ex2); 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * Uploads a file at the specified path with the data from the InputStream 79 | * 80 | * @param inputStream InputStream of the file which should be uploaded 81 | * @param remotePath path where the file should be uploaded to 82 | */ 83 | public void uploadFile(InputStream inputStream, String remotePath) { 84 | uploadFile(inputStream, remotePath, true); 85 | } 86 | 87 | /** 88 | * Uploads a file at the specified path with the data from the InputStream 89 | * with an additional continue header 90 | * 91 | * @param inputStream InputStream of the file which should be uploaded 92 | * @param remotePath path where the file should be uploaded to 93 | * @param continueHeader Continue header is added to receive a possible error by the server before any data is sent. 94 | */ 95 | public void uploadFile(InputStream inputStream, String remotePath, boolean continueHeader) { 96 | String path = buildWebdavPath(remotePath); 97 | 98 | Sardine sardine = buildAuthSardine(); 99 | 100 | try 101 | { 102 | sardine.put(path, inputStream, null, continueHeader); 103 | } catch (IOException e) 104 | { 105 | throw new NextcloudApiException(e); 106 | } 107 | finally 108 | { 109 | try 110 | { 111 | sardine.shutdown(); 112 | } 113 | catch(Exception ex2) 114 | { 115 | LOG.warn(ERROR_SHUTDOWN, ex2); 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * method to remove files 122 | * 123 | * @param remotePath path of the file which should be removed 124 | */ 125 | public void removeFile(String remotePath) { 126 | deletePath(remotePath); 127 | } 128 | 129 | /** 130 | * method to rename/move files 131 | * 132 | * @param oldPath path of the file which should be renamed/moved 133 | * @param newPath path of the file which should be renamed/moved 134 | * @param overwriteExisting overwrite if target already exists 135 | */ 136 | public void renameFile(String oldPath, String newPath, boolean overwriteExisting) { 137 | renamePath(oldPath, newPath, overwriteExisting); 138 | } 139 | 140 | 141 | /** 142 | * Downloads the file at the specified remotepath to the download directory, 143 | * and returns true if the download is successful 144 | * 145 | * @param remotePath Remotepath where the file is saved in the nextcloud 146 | * server 147 | * @param downloadDirPath Path where the file is downloaded, it would be 148 | * created if it doesn't exist. 149 | * @return boolean 150 | * @throws IOException In case of IO errors 151 | */ 152 | public boolean downloadFile(String remotePath, String downloadDirPath) throws IOException { 153 | boolean status; 154 | String path = buildWebdavPath(remotePath); 155 | Sardine sardine = buildAuthSardine(); 156 | 157 | File downloadFilepath = new File(downloadDirPath); 158 | if (!downloadFilepath.exists()) 159 | { 160 | downloadFilepath.mkdir(); 161 | } 162 | 163 | if (fileExists(remotePath)) 164 | { 165 | //Extract the Filename from the path 166 | String[] segments = remotePath.split("/"); 167 | String filename = segments[segments.length - 1]; 168 | downloadDirPath = downloadDirPath + "/" + filename; 169 | } 170 | try (InputStream in = sardine.get(path)) { 171 | byte[] buffer = new byte[AWebdavHandler.FILE_BUFFER_SIZE]; 172 | int bytesRead; 173 | File targetFile = new File(downloadDirPath); 174 | try (OutputStream outStream = java.nio.file.Files.newOutputStream(targetFile.toPath())) { 175 | while ((bytesRead = in.read(buffer)) != -1) { 176 | outStream.write(buffer, 0, bytesRead); 177 | } 178 | outStream.flush(); 179 | } 180 | status = true; 181 | } catch (IOException e) { 182 | throw new NextcloudApiException(e); 183 | } finally { 184 | sardine.shutdown(); 185 | } 186 | return status; 187 | } 188 | 189 | /** 190 | * Downloads the file at the specified remotepath as an InputStream, 191 | * 192 | * @param remotePath Remotepath where the file is saved in the nextcloud 193 | * server 194 | * @return InputStream 195 | */ 196 | public InputStream downloadFile(String remotePath) { 197 | String path = buildWebdavPath(remotePath); 198 | Sardine sardine = buildAuthSardine(); 199 | 200 | WebdavInputStream in; 201 | try 202 | { 203 | in = new WebdavInputStream(sardine, sardine.get(path)); 204 | } catch (IOException e) 205 | { 206 | throw new NextcloudApiException(e); 207 | } 208 | finally 209 | { 210 | try 211 | { 212 | sardine.shutdown(); 213 | } 214 | catch(Exception ex2) 215 | { 216 | LOG.warn(ERROR_SHUTDOWN, ex2); 217 | } 218 | } 219 | return in; 220 | } 221 | 222 | /** 223 | * Get the properties of a resource, File or Folder 224 | * 225 | * @param remotePath Remotepath of the resource to query for 226 | * server 227 | * @param allProperties Return all properties, not only base properties 228 | * ... 229 | * @return InputStream 230 | */ 231 | public ResourceProperties getProperties(String remotePath, boolean allProperties) { 232 | String path = buildWebdavPath(remotePath); 233 | Sardine sardine = buildAuthSardine(); 234 | 235 | try 236 | { 237 | Set props= new HashSet<>(); 238 | if (allProperties) 239 | { 240 | props.add(new QName(URI_NS_DAV, "getlastmodified", "d")); 241 | props.add(new QName(URI_NS_DAV, "getetag", "d")); 242 | props.add(new QName(URI_NS_DAV, "getcontenttype", "d")); 243 | props.add(new QName(URI_NS_DAV, "resourcetype", "d")); 244 | props.add(new QName(URI_NS_DAV, "getcontentlength", "d")); 245 | props.add(new QName(URI_NS_DAV, "displayname", "d")); 246 | props.add(new QName(URI_NS_OWNCLOUD, "id", "oc")); 247 | props.add(new QName(URI_NS_OWNCLOUD, "fileid", "oc")); 248 | props.add(new QName(URI_NS_OWNCLOUD, "favorite", "oc")); 249 | props.add(new QName(URI_NS_OWNCLOUD, "comments-href", "oc")); 250 | props.add(new QName(URI_NS_OWNCLOUD, "comments-count", "oc")); 251 | props.add(new QName(URI_NS_OWNCLOUD, "comments-unread", "oc")); 252 | props.add(new QName(URI_NS_OWNCLOUD, "owner-id", "oc")); 253 | props.add(new QName(URI_NS_OWNCLOUD, "owner-display-name", "oc")); 254 | props.add(new QName(URI_NS_OWNCLOUD, "share-types", "oc")); 255 | props.add(new QName(URI_NS_OWNCLOUD, "checksums", "oc")); 256 | props.add(new QName("http://nextcloud.org/ns", "has-preview", "nc")); 257 | props.add(new QName(URI_NS_OWNCLOUD, "permissions", "oc")); 258 | props.add(new QName(URI_NS_OWNCLOUD, "size", "oc")); 259 | } 260 | List resources= sardine.propfind(path, 0, props); 261 | if (resources != null && resources.size() == 1) 262 | { 263 | DavResource res= resources.get(0); 264 | ResourceProperties retVal= new ResourceProperties(); 265 | retVal.setModified(res.getModified()); 266 | retVal.setEtag(res.getEtag()); 267 | retVal.setContentType(res.getContentType()); 268 | retVal.setContentLength(res.getContentLength()); 269 | retVal.setCreation(res.getCreation()); 270 | retVal.setDisplayName(res.getDisplayName()); 271 | if (allProperties) 272 | { 273 | Map custProps= res.getCustomProps(); 274 | retVal.setResourceType(custProps.get("resourcetype")); 275 | retVal.setId(custProps.get("id")); 276 | retVal.setFileId(custProps.get("fileid")); 277 | retVal.setFavorite("1".equals(custProps.get("favorite"))); 278 | retVal.setCommentsHref(custProps.get("comments-href")); 279 | retVal.setCommentsCount( convertStringToLong(custProps.get("comments-count"))); 280 | retVal.setCommentsUnread(convertStringToLong(custProps.get("comments-unread"))); 281 | retVal.setOwnerId(custProps.get("owner-id")); 282 | retVal.setOwnerDisplayName(custProps.get("owner-display-name")); 283 | retVal.setShareTypes(custProps.get("share-types")); 284 | retVal.setChecksums(custProps.get("checksums")); 285 | retVal.setHasPreview("1".equals(custProps.get("has-preview"))); 286 | retVal.setPermissions(custProps.get("permissions")); 287 | retVal.setSize(convertStringToLong(custProps.get("size"))); 288 | } 289 | return retVal; 290 | } 291 | else 292 | { 293 | throw new NextcloudApiException("Unexpected number of resources received"); 294 | } 295 | } catch (IOException e) 296 | { 297 | throw new NextcloudApiException(e); 298 | } 299 | finally 300 | { 301 | try 302 | { 303 | sardine.shutdown(); 304 | } 305 | catch(Exception ex2) 306 | { 307 | LOG.warn(ERROR_SHUTDOWN, ex2); 308 | } 309 | } 310 | } 311 | 312 | private long convertStringToLong(String number) 313 | { 314 | if (number == null || number.isEmpty()) 315 | { 316 | return 0; 317 | } 318 | else 319 | { 320 | return Long.parseLong(number); 321 | } 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/Folders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav; 18 | 19 | 20 | import com.github.sardine.DavResource; 21 | import com.github.sardine.Sardine; 22 | import java.io.File; 23 | import java.io.InputStream; 24 | import java.io.OutputStream; 25 | import java.io.IOException; 26 | import java.nio.file.Files; 27 | import java.util.LinkedList; 28 | import java.util.List; 29 | import org.aarboard.nextcloud.api.ServerConfig; 30 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | /** 35 | * 36 | * @author a.schild 37 | */ 38 | public class Folders extends AWebdavHandler{ 39 | 40 | private static final Logger LOG = LoggerFactory.getLogger(Folders.class); 41 | public static final String ERROR_SARDINE_CLOSING = "error in closing sardine connector"; 42 | 43 | public Folders(ServerConfig serverConfig) { 44 | super(serverConfig); 45 | } 46 | 47 | /** 48 | * Get all subfolders of the specified path 49 | * 50 | * @param remotePath path of the folder 51 | * @return found subfolders 52 | * 53 | * @deprecated The methods naming is somehow misleading, as it lists 54 | * all resources (subfolders and files) within the given {@code rootPath}. 55 | * Please use {@link #listFolderContent(String)} instead. 56 | */ 57 | @Deprecated 58 | public List getFolders(String remotePath) 59 | { 60 | return listFolderContent(remotePath); 61 | } 62 | 63 | /** 64 | * List all file names and subfolders of the specified path 65 | * 66 | * @param remotePath path of the folder 67 | * @return found file names and subfolders 68 | */ 69 | public List listFolderContent(String remotePath) 70 | { 71 | return listFolderContent(remotePath, 1); 72 | } 73 | 74 | /** 75 | * List all file names and subfolders of the specified path traversing 76 | * into subfolders to the given depth. 77 | * 78 | * @param remotePath path of the folder 79 | * @param depth depth of recursion while listing folder contents 80 | * @return found file names and subfolders 81 | */ 82 | public List listFolderContent(String remotePath, int depth) 83 | { 84 | return listFolderContent(remotePath, depth, false, false); 85 | } 86 | 87 | /** 88 | * List all file names and subfolders of the specified path traversing 89 | * into subfolders to the given depth. 90 | * 91 | * @param remotePath path of the folder 92 | * @param depth depth of recursion while listing folder contents 93 | * @param excludeFolderNames excludes the folder names from the list 94 | * @param returnFullPath return full path to files, not only filename 95 | * like folder1/folder1/file.txt 96 | * @return found file names and subfolders 97 | */ 98 | public List listFolderContent(String remotePath, int depth, boolean excludeFolderNames, boolean returnFullPath) 99 | { 100 | String pathPrefix = getWebdavPathPrefix(); 101 | String path = buildWebdavPath(remotePath); 102 | 103 | List retVal = new LinkedList<>(); 104 | Sardine sardine = buildAuthSardine(); 105 | List resources; 106 | try { 107 | resources = sardine.list(path, depth); 108 | } catch (IOException e) { 109 | throw new NextcloudApiException(e); 110 | } 111 | finally 112 | { 113 | try 114 | { 115 | sardine.shutdown(); 116 | } 117 | catch (IOException ex) 118 | { 119 | LOG.warn(ERROR_SARDINE_CLOSING, ex); 120 | } 121 | } 122 | for (DavResource res : resources) 123 | { 124 | if (excludeFolderNames && res.isDirectory()) { 125 | // Dont' return folders 126 | } 127 | else { 128 | if (returnFullPath) 129 | { 130 | if (res.getPath().startsWith(pathPrefix)) 131 | { 132 | retVal.add(res.getPath().substring(pathPrefix.length())); 133 | } 134 | else 135 | { 136 | LOG.error("Unhandled edge case, path prefix {} does not match built prefix {}", res.getPath(), path); 137 | retVal.add(res.getPath()); 138 | } 139 | } 140 | else 141 | { 142 | retVal.add(res.getName()); 143 | } 144 | } 145 | } 146 | return retVal; 147 | } 148 | 149 | /** 150 | * Checks if the folder at the specified path exists 151 | * 152 | * @param remotePath path of the folder 153 | * @return true if the folder exists 154 | */ 155 | public boolean exists(String remotePath) 156 | { 157 | return pathExists(remotePath); 158 | } 159 | 160 | /** 161 | * Creates a folder at the specified path 162 | * 163 | * @param remotePath path of the folder 164 | */ 165 | public void createFolder(String remotePath) 166 | { 167 | String path= buildWebdavPath(remotePath ); 168 | Sardine sardine = buildAuthSardine(); 169 | 170 | try { 171 | sardine.createDirectory(path); 172 | } catch (IOException e) { 173 | throw new NextcloudApiException(e); 174 | } 175 | finally 176 | { 177 | try 178 | { 179 | sardine.shutdown(); 180 | } 181 | catch (IOException ex) 182 | { 183 | LOG.warn(ERROR_SARDINE_CLOSING, ex); 184 | } 185 | } 186 | } 187 | 188 | /** 189 | * Deletes the folder at the specified path 190 | * 191 | * @param remotePath path of the folder 192 | */ 193 | public void deleteFolder(String remotePath) 194 | { 195 | deletePath(remotePath); 196 | } 197 | 198 | /** 199 | * method to rename/move folder 200 | * 201 | * @param oldPath path of the folder which should be renamed/moved 202 | * @param newPath path of the folder which should be renamed/moved 203 | * @param overwriteExisting overwrite if target already exists 204 | */ 205 | public void renameFolder(String oldPath, String newPath, boolean overwriteExisting) { 206 | renamePath(oldPath, newPath, overwriteExisting); 207 | } 208 | 209 | /** 210 | * Downloads the folder at the specified remotePath to the rootDownloadDirPath 211 | * 212 | * @param remotePath the path in the nextcloud server with respect to the specific folder 213 | * @param rootDownloadDirPath the local path in the system where the folder needs be saved 214 | * @throws IOException In case of IO errors 215 | */ 216 | public void downloadFolder(String remotePath, String rootDownloadDirPath) throws IOException { 217 | int depth=1; 218 | String[] segments = remotePath.split("/"); 219 | String folderName = segments[segments.length - 1]; 220 | String newDownloadDir = rootDownloadDirPath + "/" + folderName; 221 | File nefile1 = new File(newDownloadDir); 222 | if(!nefile1.exists()) { 223 | LOG.info("Creating new download directory: {}", newDownloadDir); 224 | nefile1.mkdir(); 225 | } 226 | 227 | String listPathURL = buildWebdavPath(remotePath); 228 | int count = 0; 229 | String filePath; 230 | List retVal= new LinkedList<>(); 231 | List resources; 232 | Sardine sardine = buildAuthSardine(); 233 | try 234 | { 235 | try { 236 | resources = sardine.list(listPathURL, depth); 237 | } catch (IOException e) { 238 | throw new NextcloudApiException(e); 239 | } 240 | 241 | for (DavResource res : resources) 242 | { 243 | //Skip the Documents folder which is listed as default as first by the sardine output 244 | if(count != 0) { 245 | if(res.isDirectory()) { 246 | String fileName = res.getName(); 247 | String pathtosend = remotePath + "/" + fileName; 248 | downloadFolder(pathtosend,newDownloadDir); 249 | } 250 | else { 251 | String fileName = res.getName(); 252 | filePath = buildWebdavPath(remotePath + "/" + fileName); 253 | retVal.add(res.getName()); 254 | 255 | InputStream in; 256 | if (sardine.exists(filePath)) { 257 | in = sardine.get(filePath); 258 | byte[] buffer = new byte[AWebdavHandler.FILE_BUFFER_SIZE]; 259 | int bytesRead; 260 | File targetFile = new File(newDownloadDir + "/" + fileName); 261 | try (OutputStream outStream = Files.newOutputStream( 262 | targetFile.toPath())) 263 | { 264 | while ((bytesRead = in.read(buffer)) != -1) 265 | { 266 | outStream.write(buffer, 0, bytesRead); 267 | } 268 | outStream.flush(); 269 | } 270 | } 271 | } 272 | } 273 | count ++; 274 | } 275 | } 276 | finally { 277 | try 278 | { 279 | sardine.shutdown(); 280 | } 281 | catch (IOException ex) 282 | { 283 | LOG.warn(ERROR_SARDINE_CLOSING, ex); 284 | } 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/ResourceProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav; 18 | 19 | import java.util.Date; 20 | 21 | /** 22 | * 23 | * @author a.schild 24 | */ 25 | public class ResourceProperties { 26 | private long contentLength= -1; 27 | private String contentType= null; 28 | private Date creation= null; 29 | private String displayName= null; 30 | private String etag= null; 31 | private Date modified= null; 32 | 33 | private String resourceType; 34 | private String id; 35 | private String fileId; 36 | private boolean favorite; 37 | private String commentsHref; 38 | private long commentsCount; 39 | private long commentsUnread; 40 | private String ownerId; 41 | private String ownerDisplayName; 42 | private String shareTypes; 43 | private String permissions; 44 | private String checksums; 45 | private boolean hasPreview; 46 | private long size; // Unlike contentlength, this property also works for folders reporting the size of everything in the folder 47 | 48 | public ResourceProperties() { 49 | //no-op 50 | } 51 | 52 | 53 | /** 54 | * @return the modified 55 | */ 56 | public Date getModified() { 57 | return modified; 58 | } 59 | 60 | /** 61 | * @param modified the modified to set 62 | */ 63 | public void setModified(Date modified) { 64 | this.modified = modified; 65 | } 66 | 67 | /** 68 | * @return the contentType 69 | */ 70 | public String getContentType() { 71 | return contentType; 72 | } 73 | 74 | /** 75 | * @param contentType the contentType to set 76 | */ 77 | public void setContentType(String contentType) { 78 | this.contentType = contentType; 79 | } 80 | 81 | /** 82 | * @return the etag 83 | */ 84 | public String getEtag() { 85 | return etag; 86 | } 87 | 88 | /** 89 | * @param etag the etag to set 90 | */ 91 | public void setEtag(String etag) { 92 | this.etag = etag; 93 | } 94 | 95 | /** 96 | * @return the displayName 97 | */ 98 | public String getDisplayName() { 99 | return displayName; 100 | } 101 | 102 | /** 103 | * @param displayName the displayName to set 104 | */ 105 | public void setDisplayName(String displayName) { 106 | this.displayName = displayName; 107 | } 108 | 109 | /** 110 | * @return the contentLength 111 | */ 112 | public long getContentLength() { 113 | return contentLength; 114 | } 115 | 116 | /** 117 | * @param contentLength the contentLength to set 118 | */ 119 | public void setContentLength(long contentLength) { 120 | this.contentLength = contentLength; 121 | } 122 | 123 | /** 124 | * @return the creation 125 | */ 126 | public Date getCreation() { 127 | return creation; 128 | } 129 | 130 | /** 131 | * @param creation the creation to set 132 | */ 133 | public void setCreation(Date creation) { 134 | this.creation = creation; 135 | } 136 | 137 | /** 138 | * @return the resourceType 139 | */ 140 | public String getResourceType() { 141 | return resourceType; 142 | } 143 | 144 | /** 145 | * @param resourceType the resourceType to set 146 | */ 147 | public void setResourceType(String resourceType) { 148 | this.resourceType = resourceType; 149 | } 150 | 151 | /** 152 | * @return the id 153 | */ 154 | public String getId() { 155 | return id; 156 | } 157 | 158 | /** 159 | * @param id the id to set 160 | */ 161 | public void setId(String id) { 162 | this.id = id; 163 | } 164 | 165 | /** 166 | * @return the fileId 167 | */ 168 | public String getFileId() { 169 | return fileId; 170 | } 171 | 172 | /** 173 | * @param fileId the fileId to set 174 | */ 175 | public void setFileId(String fileId) { 176 | this.fileId = fileId; 177 | } 178 | 179 | /** 180 | * @return the favorite 181 | */ 182 | public boolean isFavorite() { 183 | return favorite; 184 | } 185 | 186 | /** 187 | * @param favorite the favorite to set 188 | */ 189 | public void setFavorite(boolean favorite) { 190 | this.favorite = favorite; 191 | } 192 | 193 | /** 194 | * @return the commentsHref 195 | */ 196 | public String getCommentsHref() { 197 | return commentsHref; 198 | } 199 | 200 | /** 201 | * @param commentsHref the commentsHref to set 202 | */ 203 | public void setCommentsHref(String commentsHref) { 204 | this.commentsHref = commentsHref; 205 | } 206 | 207 | /** 208 | * @return the commentsCount 209 | */ 210 | public long getCommentsCount() { 211 | return commentsCount; 212 | } 213 | 214 | /** 215 | * @param commentsCount the commentsCount to set 216 | */ 217 | public void setCommentsCount(long commentsCount) { 218 | this.commentsCount = commentsCount; 219 | } 220 | 221 | /** 222 | * @return the commentsUnread 223 | */ 224 | public long getCommentsUnread() { 225 | return commentsUnread; 226 | } 227 | 228 | /** 229 | * @param commentsUnread the commentsUnread to set 230 | */ 231 | public void setCommentsUnread(long commentsUnread) { 232 | this.commentsUnread = commentsUnread; 233 | } 234 | 235 | /** 236 | * @return the ownerId 237 | */ 238 | public String getOwnerId() { 239 | return ownerId; 240 | } 241 | 242 | /** 243 | * @param ownerId the ownerId to set 244 | */ 245 | public void setOwnerId(String ownerId) { 246 | this.ownerId = ownerId; 247 | } 248 | 249 | /** 250 | * @return the ownerDisplayName 251 | */ 252 | public String getOwnerDisplayName() { 253 | return ownerDisplayName; 254 | } 255 | 256 | /** 257 | * @param ownerDisplayName the ownerDisplayName to set 258 | */ 259 | public void setOwnerDisplayName(String ownerDisplayName) { 260 | this.ownerDisplayName = ownerDisplayName; 261 | } 262 | 263 | /** 264 | * @return the shareTypes 265 | */ 266 | public String getShareTypes() { 267 | return shareTypes; 268 | } 269 | 270 | /** 271 | * @param shareTypes the shareTypes to set 272 | */ 273 | public void setShareTypes(String shareTypes) { 274 | this.shareTypes = shareTypes; 275 | } 276 | 277 | /** 278 | * @return the checksums 279 | */ 280 | public String getChecksums() { 281 | return checksums; 282 | } 283 | 284 | /** 285 | * @param checksums the checksums to set 286 | */ 287 | public void setChecksums(String checksums) { 288 | this.checksums = checksums; 289 | } 290 | 291 | /** 292 | * @return the hasPreview 293 | */ 294 | public boolean isHasPreview() { 295 | return hasPreview; 296 | } 297 | 298 | /** 299 | * @param hasPreview the hasPreview to set 300 | */ 301 | public void setHasPreview(boolean hasPreview) { 302 | this.hasPreview = hasPreview; 303 | } 304 | 305 | /** 306 | * @return the size 307 | */ 308 | public long getSize() { 309 | return size; 310 | } 311 | 312 | /** 313 | * @param size the size to set 314 | */ 315 | public void setSize(long size) { 316 | this.size = size; 317 | } 318 | 319 | /** 320 | * @return the permissions 321 | */ 322 | public String getPermissions() { 323 | return permissions; 324 | } 325 | 326 | /** 327 | * @param permissions the permissions to set 328 | */ 329 | public void setPermissions(String permissions) { 330 | this.permissions = permissions; 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/pathresolver/NextcloudVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 scm11361 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav.pathresolver; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.text.MessageFormat; 22 | import java.util.Properties; 23 | import org.aarboard.nextcloud.api.exception.NextcloudApiException; 24 | 25 | /** 26 | *

27 | * Nextcloud Version represent the version of the nextcloud server. It covers 28 | * path behaviours of different versions. 29 | *

30 | * Get a new instance: 31 | *
32 | * 33 | * NextcloudVersion version = NextcloudVersion.get("20.0.4"); 34 | * 35 | * 36 | * @since 11.5 37 | */ 38 | public class NextcloudVersion 39 | { 40 | 41 | private static final String PROPERTIES_PATH_PATTERN = "/org/aarboard/nextcloud/api/webdav/pathresolver/webdavpathresolver_{0}.properties"; 42 | private static final String APPEND_USERNAME_PATTERN = "nextcloud.webdav.base.{0}.suffix.append.username"; 43 | 44 | private final String versionValue; 45 | 46 | /** 47 | * defaults to 20 48 | */ 49 | private Integer compatibleVersion = 20; 50 | private String major = "0"; 51 | private String minor = "0"; 52 | private String patch = "0"; 53 | private String revision = "0"; 54 | 55 | private final Properties configProperties = new Properties(); 56 | /** 57 | * defaults to files 58 | */ 59 | private WebDavPathResolverBuilder.TYPE webdavType = WebDavPathResolverBuilder.TYPE.FILES; 60 | 61 | NextcloudVersion(String versionValue) 62 | { 63 | this.versionValue = versionValue; 64 | paresVersionString(versionValue); 65 | loadValues(); 66 | } 67 | 68 | /** 69 | * static create factory method 70 | * Value need to be of form 71 | * major.minor.path(.revision) E.g. 20.4.0.0 or 14.1.3 72 | * 73 | * @param value of NextCloudInstance 74 | * @return a version instance 75 | * @since 11.5 76 | */ 77 | public static NextcloudVersion get(final String value) 78 | { 79 | if (null == value) 80 | { 81 | throw new IllegalArgumentException("Version value cannot be null !"); 82 | } 83 | 84 | if (value.trim().isEmpty()) 85 | { 86 | throw new IllegalArgumentException("Version value cannot be empty !"); 87 | } 88 | 89 | return new NextcloudVersion(value); 90 | } 91 | 92 | public NextcloudVersion ofType(WebDavPathResolverBuilder.TYPE type) 93 | { 94 | this.webdavType = type; 95 | 96 | return this; 97 | } 98 | 99 | public String getWebdavBasePath() 100 | { 101 | return configProperties.getProperty("nextcloud.webdav.base.path"); 102 | } 103 | 104 | /** 105 | * 106 | * @return true if username shall be appended to path, false otherwise 107 | */ 108 | public boolean isAppendUserName() 109 | { 110 | return Boolean.parseBoolean(configProperties.getProperty(MessageFormat.format(APPEND_USERNAME_PATTERN, this.webdavType.name().toLowerCase()), "false")); 111 | } 112 | 113 | /** 114 | * 115 | * @return true if appendSuffix shall be appended to path, false otherwise 116 | */ 117 | public boolean isAppendSuffix() 118 | { 119 | return Boolean.parseBoolean(configProperties.getProperty(MessageFormat.format(APPEND_USERNAME_PATTERN, this.webdavType.name().toLowerCase()), "false")); 120 | } 121 | 122 | @Override 123 | public String toString() 124 | { 125 | return major + "." + minor + "." + patch + "." + revision + ", compatible with [" + compatibleVersion + "]"; 126 | } 127 | 128 | private void paresVersionString(String value) 129 | { 130 | String[] values = value.split("\\."); 131 | if (values.length > 0) 132 | { 133 | major = values[0]; 134 | compatibleVersion = Integer.parseInt(major); 135 | } 136 | 137 | if (values.length > 1) 138 | { 139 | minor = values[1]; 140 | } 141 | 142 | if (values.length > 2) 143 | { 144 | patch = values[2]; 145 | } 146 | 147 | if (values.length > 3) 148 | { 149 | revision = values[3]; 150 | } 151 | 152 | if (compatibleVersion > 14) 153 | { 154 | compatibleVersion = 20; 155 | } 156 | else 157 | { 158 | compatibleVersion = 14; 159 | } 160 | 161 | } 162 | 163 | private void loadValues() 164 | { 165 | try (InputStream inStream = getClass().getResourceAsStream(MessageFormat.format(PROPERTIES_PATH_PATTERN, compatibleVersion.toString()))) 166 | { 167 | configProperties.load(inStream); 168 | } 169 | catch (IOException ex) 170 | { 171 | throw new NextcloudApiException(ex); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/pathresolver/PathHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 aschi 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav.pathresolver; 18 | 19 | /** 20 | * 21 | * @author aschi 22 | */ 23 | public class PathHelper { 24 | /** 25 | * Concat path elements and make sure we have everything enclosed 26 | * with on / , including one at the start and (optional) end 27 | * 28 | * 29 | * @param endWithSlash 30 | * @param p1 31 | * @param elements 32 | * @return 33 | */ 34 | public static String concatPathElements( 35 | boolean endWithSlash, 36 | String p1, 37 | String ... elements) 38 | { 39 | StringBuilder sb= new StringBuilder(); 40 | if (!p1.startsWith("/")) 41 | { 42 | sb.append('/'); 43 | } 44 | sb.append(p1); 45 | 46 | if (elements != null) 47 | { 48 | for (String rp : elements) 49 | { 50 | if (rp != null && !rp.isEmpty()) 51 | { 52 | if (sb.length()>0 ) 53 | { 54 | if (sb.charAt(sb.length()-1) == '/') 55 | { 56 | // Previous element ends with / 57 | if (rp.charAt(0) == '/') 58 | { 59 | // Ok, add everything, without the first char 60 | sb.append(rp.substring(1)); 61 | } 62 | else 63 | { 64 | // Ok, add everything 65 | sb.append(rp); 66 | } 67 | } 68 | else 69 | { 70 | if (sb.charAt(sb.length()-1) != '/' && 71 | rp.charAt(0) != '/') 72 | { 73 | sb.append('/'); 74 | } 75 | sb.append(rp); 76 | } 77 | } 78 | else 79 | { 80 | if (rp.charAt(0) == '/') 81 | { 82 | // Ok, add everything 83 | sb.append(rp); 84 | } 85 | else 86 | { 87 | // Ok, add everything 88 | sb.append('/'); 89 | sb.append(rp); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | // Final / if needed 96 | if (endWithSlash && sb.charAt(sb.length()-1) != '/') 97 | { 98 | sb.append('/'); 99 | } 100 | if (!endWithSlash && sb.charAt(sb.length()-1) == '/') 101 | { 102 | sb.deleteCharAt(sb.length()-1); 103 | } 104 | return sb.toString(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/pathresolver/WebDavPathResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 scm11361 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav.pathresolver; 18 | 19 | /** 20 | * @since 11.5 21 | */ 22 | public interface WebDavPathResolver 23 | { 24 | 25 | /** 26 | * Calculates the webdav server path. E.g.: 27 | * nextcloud/remote.php/dav/files/username 28 | * nextcloud/remote.php/dav/files/username/myfolder 29 | * 30 | * @param remotePaths Subpath elements to append 31 | * @return the webdav path 32 | */ 33 | String getWebDavPath(String... remotePaths); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/pathresolver/WebDavPathResolverBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 scm11361 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav.pathresolver; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * @since 11.5 24 | */ 25 | public class WebDavPathResolverBuilder 26 | { 27 | 28 | public static final String NEXTCLOUD_WEBDAV_BASE_PATH = "nextcloud.webdav.base.path"; 29 | 30 | public static final String NEXTCLOUD_WEBDAV_BASE_PATH_SUFFIX = "nextcloud.webdav.base.suffix.path"; 31 | 32 | public static final String NEXTCLOUD_WEBDAV_BASE_PATH_PREFIX = "nextcloud.webdav.base.prefix.path"; 33 | 34 | public static final String NEXTCLOUD_USER_NAME = "nextcloud.userName"; 35 | 36 | /** 37 | * Type of the resolver FILES, VCARD, CALDAV, VERSION 38 | */ 39 | public enum TYPE 40 | { 41 | /** 42 | * specifies to calculate the files path of the nextcloud instance 43 | */ 44 | FILES("files"), 45 | /** 46 | * VCARD path 47 | */ 48 | VCARD("addressbooks/users"), 49 | /** 50 | * CALDAV 51 | */ 52 | CALDAV("calendars"), 53 | /** 54 | * specifies to calculate the Version path of the nextcloud instance 55 | */ 56 | VERSION(""); 57 | 58 | private final String suffix; 59 | 60 | private TYPE(String suffix) 61 | { 62 | this.suffix = suffix; 63 | } 64 | 65 | public String getSuffix() 66 | { 67 | return suffix; 68 | } 69 | 70 | } 71 | 72 | private final TYPE type; 73 | 74 | /** 75 | * Defaults to the latest 76 | */ 77 | private NextcloudVersion version = NextcloudVersion.get("20.0.4"); 78 | 79 | private final Map valueMap = new HashMap<>(); 80 | 81 | public static WebDavPathResolverBuilder get(final TYPE type) 82 | { 83 | return new WebDavPathResolverBuilder(type); 84 | } 85 | 86 | WebDavPathResolverBuilder(final TYPE type) 87 | { 88 | this.type = type; 89 | } 90 | 91 | /** 92 | * 93 | * @param version of the nextcloud instance 94 | * @return the WebDavPathResolverBuilder 95 | */ 96 | public WebDavPathResolverBuilder ofVersion(NextcloudVersion version) 97 | { 98 | this.version = version.ofType(type); 99 | return this; 100 | } 101 | 102 | /** 103 | * The remoteuser 104 | * 105 | * @param user the remoteuser (Must be the username and not 106 | * the login name if they are not identical) 107 | * @return the WebDavPathResolverBuilder 108 | */ 109 | public WebDavPathResolverBuilder withUserName(String user) 110 | { 111 | if (null != user) 112 | { 113 | valueMap.put(NEXTCLOUD_USER_NAME, user); 114 | } 115 | return this; 116 | } 117 | 118 | /** 119 | * E.g. nextcloud 120 | * 121 | * @param suffix of the basepath 122 | * @return the WebDavPathResolverBuilder 123 | */ 124 | public WebDavPathResolverBuilder withBasePathSuffix(String suffix) 125 | { 126 | if (null != suffix) 127 | { 128 | valueMap.put(NEXTCLOUD_WEBDAV_BASE_PATH_SUFFIX, suffix); 129 | } 130 | 131 | return this; 132 | } 133 | 134 | /** 135 | * E.g. files,calendars 136 | * 137 | * @param prefix the suffix of the base path 138 | * @return the WebDavPathResolverBuilder 139 | */ 140 | public WebDavPathResolverBuilder withBasePathPrefix(String prefix) 141 | { 142 | if (null != prefix) 143 | { 144 | valueMap.put(NEXTCLOUD_WEBDAV_BASE_PATH_PREFIX, prefix); 145 | } 146 | 147 | return this; 148 | } 149 | 150 | /** 151 | * E.g. remote.php/dav 152 | * 153 | * @param path of the nextcloud webdav interface 154 | * @return the WebDavPathResolverBuilder 155 | */ 156 | public WebDavPathResolverBuilder withBasePath(String path) 157 | { 158 | if (null == path) 159 | { 160 | throw new IllegalArgumentException("WebDav base path cannot be null!"); 161 | } 162 | 163 | valueMap.put(NEXTCLOUD_WEBDAV_BASE_PATH, path); 164 | return this; 165 | } 166 | 167 | /** 168 | * builds the resolver 169 | * 170 | * @return the resolver 171 | * @see WebDavPathResolver 172 | */ 173 | public WebDavPathResolver build() 174 | { 175 | WebDavPathResolver result; 176 | switch (type) 177 | { 178 | case FILES: 179 | case CALDAV: 180 | case VCARD: 181 | result = new FolderWebDavPathResolverImpl(valueMap.getOrDefault(NEXTCLOUD_WEBDAV_BASE_PATH, version.getWebdavBasePath())) 182 | .setPathPrefix(valueMap.getOrDefault(NEXTCLOUD_WEBDAV_BASE_PATH_PREFIX, "")) 183 | .setPathSuffix(version.isAppendSuffix() ? valueMap.getOrDefault(NEXTCLOUD_WEBDAV_BASE_PATH_SUFFIX, type.getSuffix()) : "") 184 | .setUserName(version.isAppendUserName() ? valueMap.getOrDefault(NEXTCLOUD_USER_NAME, "") : ""); 185 | 186 | break; 187 | case VERSION: 188 | result = new WebDavpathResolverVersionImpl(valueMap.getOrDefault(NEXTCLOUD_WEBDAV_BASE_PATH, "status.php"))// 189 | .setPathPrefix(valueMap.getOrDefault(NEXTCLOUD_WEBDAV_BASE_PATH_PREFIX, "")); 190 | break; 191 | default: 192 | result = null; 193 | break; 194 | 195 | } 196 | 197 | return result; 198 | } 199 | 200 | private static class FolderWebDavPathResolverImpl implements WebDavPathResolver 201 | { 202 | 203 | private String webDavBasePath; 204 | private String userName; 205 | private String pathSuffix; 206 | private String pathPrefix; 207 | private String webdavPath = null; 208 | 209 | public FolderWebDavPathResolverImpl(String webDavBasePath) 210 | { 211 | this.webDavBasePath = webDavBasePath; 212 | this.userName = ""; 213 | this.pathSuffix = ""; 214 | this.pathPrefix = ""; 215 | } 216 | 217 | private FolderWebDavPathResolverImpl setPathPrefix(String pathPrefix) 218 | { 219 | this.pathPrefix = pathPrefix; 220 | return this; 221 | } 222 | 223 | private FolderWebDavPathResolverImpl setWebDavBasePath(String webDavBasePath) 224 | { 225 | this.webDavBasePath = webDavBasePath; 226 | return this; 227 | } 228 | 229 | private FolderWebDavPathResolverImpl setUserName(String userName) 230 | { 231 | this.userName = userName; 232 | return this; 233 | } 234 | 235 | private FolderWebDavPathResolverImpl setPathSuffix(String pathSuffix) 236 | { 237 | this.pathSuffix = pathSuffix; 238 | return this; 239 | } 240 | 241 | private String getWebdavPath() 242 | { 243 | if (null == this.webdavPath) 244 | { 245 | // We can't use Paths here, since we need / as separator, and not a platform specific delimiter 246 | // which might be something else 247 | this.webdavPath= PathHelper.concatPathElements( 248 | true, 249 | this.pathPrefix, 250 | this.webDavBasePath, 251 | this.pathSuffix, 252 | this.userName); 253 | } 254 | return this.webdavPath; 255 | } 256 | 257 | @Override 258 | public String getWebDavPath(String ... remotePath) 259 | { 260 | return PathHelper.concatPathElements(true, getWebdavPath(), remotePath); 261 | } 262 | } 263 | 264 | } 265 | -------------------------------------------------------------------------------- /src/main/java/org/aarboard/nextcloud/api/webdav/pathresolver/WebDavpathResolverVersionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 scm11361 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.webdav.pathresolver; 18 | 19 | 20 | public class WebDavpathResolverVersionImpl implements WebDavPathResolver 21 | { 22 | 23 | private final String versionPath; 24 | 25 | private String pathPrefix; 26 | 27 | public WebDavpathResolverVersionImpl(String versionPath) 28 | { 29 | this.versionPath = versionPath; 30 | } 31 | 32 | public String getPathPrefix() 33 | { 34 | return pathPrefix; 35 | } 36 | 37 | public WebDavpathResolverVersionImpl setPathPrefix(String pathPrefix) 38 | { 39 | this.pathPrefix = pathPrefix; 40 | return this; 41 | } 42 | 43 | private String getWebdavPath() 44 | { 45 | return PathHelper.concatPathElements(true, pathPrefix, versionPath); 46 | } 47 | 48 | @Override 49 | public String getWebDavPath(String... remotePaths) { 50 | return PathHelper.concatPathElements(false, getWebdavPath(), remotePaths); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/resources/org/aarboard/nextcloud/api/webdav/pathresolver/webdavpathresolver_14.properties: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 scm11361 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | nextcloud.webdav.base.path=/remote.php/webdav/ 16 | 17 | nextcloud.webdav.base.caldav.suffix.path=calendars 18 | nextcloud.webdav.base.caldav.suffix.append.username=true 19 | nextcloud.webdav.base.vcard.suffix.path=addressbooks/users 20 | nextcloud.webdav.base.vcard.suffix.append.username=true -------------------------------------------------------------------------------- /src/main/resources/org/aarboard/nextcloud/api/webdav/pathresolver/webdavpathresolver_20.properties: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 scm11361 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | nextcloud.webdav.base.path=/remote.php/dav/ 16 | nextcloud.webdav.base.files.suffix.path=files 17 | nextcloud.webdav.base.files.suffix.append.username=true 18 | nextcloud.webdav.base.caldav.suffix.path=calendars 19 | nextcloud.webdav.base.caldav.suffix.append.username=true 20 | nextcloud.webdav.base.vcard.suffix.path=addressbooks/users 21 | nextcloud.webdav.base.vcard.suffix.append.username=true 22 | 23 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/ATestClass.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 andre 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import org.junit.AfterClass; 20 | import org.junit.Before; 21 | 22 | /** 23 | * 24 | * @author andre 25 | */ 26 | public class ATestClass { 27 | public static final String TESTUSER = "testuser"; 28 | public static final String TESTUSER_WITH_INVALID_CHAR = "test@user.org"; 29 | public static final String TESTGROUP = "testgroup"; 30 | public static final String TEST_FOLDER1 = "new-test-folder"; 31 | public static final String TEST_FOLDER1_RENAMED = "new-test-folder-renamed"; 32 | public static final String TEST_FOLDER2 = "new test folder"; 33 | public static final String TESTFILE1 = "test1.txt"; 34 | public static final String TESTFILE1_RENAMED = "test1-renamed.txt"; 35 | public static final String TESTFILE2 = "test2.txt"; 36 | public static final String TESTFILE2_RENAMED = "test2-renamed.txt"; 37 | public static final String TESTFILE3 = "test3-äöüÖÄÜ and with plus + sign \u32A2.txt"; 38 | public static final String TESTFILE4 = "test4.txt"; 39 | public static final String TESTFILE6 = "test6.txt"; 40 | public static final String TESTFILE7 = "test7#specialchar.txt"; 41 | 42 | public static String serverName = null; 43 | public static String userName = null; 44 | public static String password = null; 45 | public static int serverPort= 443; 46 | 47 | public static NextcloudConnector _nc; 48 | 49 | @Before 50 | public void setUp() { 51 | TestHelper th= new TestHelper(); 52 | serverName= th.getServerName(); 53 | userName= th.getUserName(); 54 | password= th.getPassword(); 55 | serverPort= th.getServerPort(); 56 | if (serverName != null) 57 | { 58 | _nc = new NextcloudConnector(serverName, serverPort == 443, serverPort, userName, password); 59 | } 60 | } 61 | 62 | @AfterClass 63 | public static void tearDown() { 64 | if (serverName != null) 65 | { 66 | try 67 | { 68 | _nc.removeFile(TESTFILE1); 69 | } 70 | catch (Exception ex) 71 | { 72 | // Catch any errors 73 | } 74 | try 75 | { 76 | _nc.removeFile(TESTFILE2); 77 | } 78 | catch (Exception ex) 79 | { 80 | // Catch any errors 81 | } 82 | try 83 | { 84 | _nc.removeFile(TESTFILE3); 85 | } 86 | catch (Exception ex) 87 | { 88 | // Catch any errors 89 | } 90 | } 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/TestConfigConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import static org.junit.Assert.assertNotNull; 20 | 21 | import java.util.List; 22 | import org.junit.FixMethodOrder; 23 | import org.junit.Test; 24 | import org.junit.runners.MethodSorters; 25 | 26 | /** 27 | * 28 | * @author a.schild 29 | */ 30 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 31 | public class TestConfigConnector extends ATestClass { 32 | 33 | @Test 34 | public void t01_testGetConfigApps() { 35 | System.out.println("getConfigApps"); 36 | if (_nc != null) 37 | { 38 | List apps = _nc.getAppConfigApps(); 39 | assertNotNull(apps); 40 | } 41 | } 42 | 43 | @Test 44 | public void t02_testGetConfigAppsFiles() { 45 | System.out.println("getConfigAppsFiles"); 46 | if (_nc != null) 47 | { 48 | List appKeys = _nc.getAppConfigAppKeys("files"); 49 | assertNotNull(appKeys); 50 | } 51 | } 52 | 53 | @Test 54 | public void t03_testGetConfigAppsFilesVersion() { 55 | System.out.println("getConfigAppsFiles"); 56 | if (_nc != null) 57 | { 58 | String version = _nc.getAppConfigAppKeyValue("files", "installed_version"); 59 | assertNotNull(version); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/TestFiles.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertTrue; 22 | 23 | import java.io.ByteArrayInputStream; 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import org.aarboard.nextcloud.api.webdav.ResourceProperties; 28 | import org.junit.Assert; 29 | import static org.junit.Assert.assertNotNull; 30 | import static org.junit.Assert.fail; 31 | 32 | import org.junit.FixMethodOrder; 33 | import org.junit.Test; 34 | import org.junit.runners.MethodSorters; 35 | 36 | /** 37 | * 38 | * @author a.schild 39 | */ 40 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 41 | public class TestFiles extends ATestClass { 42 | 43 | private final static String TEST1_FILE_CONTENT= "Content of Test 1 file"; 44 | private final static String TEST2_FILE_CONTENT= ""; 45 | private final static String TEST3_FILE_CONTENT= "Content of Test 3 file"; 46 | private final static String TEST4_FILE_CONTENT= ""; 47 | 48 | @Test 49 | public void t25_testUploadFile() { 50 | System.out.println("uploadFile ("+TESTFILE1+")"); 51 | if (_nc != null) 52 | { 53 | InputStream inputStream = new ByteArrayInputStream(TEST1_FILE_CONTENT.getBytes()); 54 | _nc.uploadFile(inputStream, TESTFILE1, false); 55 | } 56 | } 57 | 58 | @Test 59 | public void t25_2_testUploadFile() { 60 | System.out.println("uploadFile 0Bytes ("+TESTFILE2+")"); 61 | if (_nc != null) 62 | { 63 | ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST2_FILE_CONTENT.getBytes()); 64 | _nc.uploadFile(inputStream, TESTFILE2, false); 65 | } 66 | } 67 | 68 | @Test 69 | public void t25_3_testUploadFile() { 70 | System.out.println("uploadFile umlauts ("+TESTFILE3+")"); 71 | if (_nc != null) 72 | { 73 | InputStream inputStream = new ByteArrayInputStream(TEST3_FILE_CONTENT.getBytes()); 74 | _nc.uploadFile(inputStream, TESTFILE3, false); 75 | } 76 | } 77 | 78 | @Test 79 | public void t25_4_testUploadFile() { 80 | System.out.println("uploadFile 0Bytes ("+TESTFILE4+")"); 81 | if (_nc != null) 82 | { 83 | InputStream inputStream = new ByteArrayInputStream(TEST4_FILE_CONTENT.getBytes()); 84 | _nc.uploadFile(inputStream, TESTFILE4, true); 85 | } 86 | } 87 | 88 | @Test 89 | public void t25_6_testUploadFile() { 90 | System.out.println("uploadFile 26 Bytes ("+TESTFILE6+")"); 91 | if (_nc != null) 92 | { 93 | File srcFile= new File("src/test/resources/"+TESTFILE6); 94 | _nc.uploadFile(srcFile, TESTFILE6); 95 | } 96 | } 97 | 98 | @Test 99 | public void t25_7_testUploadFile() { 100 | System.out.println("uploadFile 26 Bytes ("+TESTFILE7+")"); 101 | if (_nc != null) 102 | { 103 | File srcFile= new File("src/test/resources/"+TESTFILE7); 104 | _nc.uploadFile(srcFile, TESTFILE7); 105 | } 106 | } 107 | 108 | @Test 109 | public void t26_testFileExists() { 110 | System.out.println("fileExists ("+TESTFILE1+")"); 111 | if (_nc != null) 112 | { 113 | boolean result = _nc.fileExists(TESTFILE1); 114 | assertTrue(result); 115 | 116 | result = _nc.fileExists("non-existing-file"); 117 | assertFalse(result); 118 | } 119 | } 120 | 121 | @Test 122 | public void t26_2_testFileExists2() { 123 | System.out.println("fileExists2 ("+TESTFILE3+")"); 124 | if (_nc != null) 125 | { 126 | boolean result = _nc.fileExists(TESTFILE3); 127 | assertTrue(result); 128 | } 129 | } 130 | 131 | @Test 132 | public void t27_2_1_testFolderProperties() { 133 | System.out.println("folderProperties all (/)"); 134 | if (_nc != null) 135 | { 136 | try 137 | { 138 | ResourceProperties props= _nc.getProperties("/", true); 139 | Assert.assertNotNull("Properties returned", props); 140 | } 141 | catch (IOException ex) 142 | { 143 | assertFalse(ex.getMessage(), false); 144 | } 145 | } 146 | } 147 | 148 | @Test 149 | public void t26_2_testFileExists4() { 150 | System.out.println("fileExists4 ("+TESTFILE4+")"); 151 | if (_nc != null) 152 | { 153 | boolean result = _nc.fileExists(TESTFILE4); 154 | assertTrue(result); 155 | } 156 | } 157 | 158 | @Test 159 | public void t26_2_testFileExists7() { 160 | System.out.println("fileExists7 ("+TESTFILE7+")"); 161 | if (_nc != null) 162 | { 163 | boolean result = _nc.fileExists(TESTFILE7); 164 | assertTrue(result); 165 | } 166 | } 167 | 168 | @Test 169 | public void t27_1_testFileProperties() { 170 | System.out.println("fileProperties basic ("+TESTFILE1+")"); 171 | if (_nc != null) 172 | { 173 | try 174 | { 175 | _nc.getProperties(TESTFILE1, false); 176 | //assertTrue(result); 177 | } 178 | catch (IOException ex) 179 | { 180 | assertFalse(ex.getMessage(), false); 181 | } 182 | } 183 | } 184 | 185 | @Test 186 | public void t27_2_testFileProperties() { 187 | System.out.println("fileProperties basic ("+TESTFILE2+")"); 188 | if (_nc != null) 189 | { 190 | try 191 | { 192 | _nc.getProperties(TESTFILE2, false); 193 | //assertTrue(result); 194 | } 195 | catch (IOException ex) 196 | { 197 | assertFalse(ex.getMessage(), false); 198 | } 199 | } 200 | } 201 | 202 | @Test 203 | public void t27_2_1_testFileProperties() { 204 | System.out.println("fileProperties all ("+TESTFILE2+")"); 205 | if (_nc != null) 206 | { 207 | try 208 | { 209 | _nc.getProperties(TESTFILE2, true); 210 | //assertTrue(result); 211 | } 212 | catch (IOException ex) 213 | { 214 | assertFalse(ex.getMessage(), false); 215 | } 216 | } 217 | } 218 | 219 | @Test 220 | public void t27_2_2_testFileProperties() { 221 | System.out.println("fileProperties partial/all ("+TESTFILE2+")"); 222 | if (_nc != null) 223 | { 224 | try 225 | { 226 | ResourceProperties propsPartial= _nc.getProperties(TESTFILE2, false); 227 | ResourceProperties propsFull= _nc.getProperties(TESTFILE2, true); 228 | assertTrue("ETAG is not identic", propsPartial.getEtag().equals(propsFull.getEtag())); 229 | } 230 | catch (IOException ex) 231 | { 232 | assertFalse(ex.getMessage(), false); 233 | } 234 | } 235 | } 236 | 237 | @Test 238 | public void t27_2_6_testFileProperties() { 239 | System.out.println("fileProperties all ("+TESTFILE6+")"); 240 | if (_nc != null) 241 | { 242 | try 243 | { 244 | ResourceProperties props= _nc.getProperties(TESTFILE6, true); 245 | assertNotNull("Retval is null", props); 246 | } 247 | catch (IOException ex) 248 | { 249 | assertFalse(ex.getMessage(), false); 250 | } 251 | } 252 | } 253 | 254 | 255 | @Test 256 | public void t27_99_testFileProperties() { 257 | System.out.println("fileProperties not existing"); 258 | if (_nc != null) 259 | { 260 | try 261 | { 262 | _nc.getProperties(TESTFILE2+"-not-existing", false); 263 | fail("Resource should throw 404 error"); 264 | } 265 | catch (Exception ex) 266 | { 267 | assertEquals( 268 | "com.github.sardine.impl.SardineException: status code: 404, reason phrase: Unexpected response (404 Not Found)", 269 | ex.getMessage()); 270 | } 271 | } 272 | } 273 | 274 | 275 | 276 | @Test 277 | public void t28_3_testDowloadFile() throws IOException { 278 | System.out.println("downloadFile umlauts as InputStream ("+TESTFILE3+")"); 279 | if (_nc != null) 280 | { 281 | InputStream is= _nc.downloadFile(TESTFILE3); 282 | int nCount= 0; 283 | while (is.read() != -1) 284 | { 285 | nCount++; 286 | } 287 | is.close(); 288 | assertEquals("Downloaded content size does not match", nCount, 289 | TEST3_FILE_CONTENT.length()); 290 | } 291 | } 292 | 293 | @Test 294 | public void t29_3_testDowloadFile() throws IOException { 295 | System.out.println("downloadFile umlauts as File ("+TESTFILE1+")"); 296 | if (_nc != null) 297 | { 298 | File of= new File(System.getProperty("java.io.tmpdir")+File.separator+TESTFILE1); 299 | if (_nc.downloadFile(TESTFILE1, of.getParent())) 300 | { 301 | assertEquals("Downloaded content size does not match", of.length(), 302 | TEST1_FILE_CONTENT.length()); 303 | } 304 | else 305 | { 306 | fail("Downloaded file failed"); 307 | } 308 | if (of.exists()) 309 | { 310 | of.delete(); 311 | } 312 | } 313 | } 314 | 315 | @Test 316 | public void t29_3_testDowloadFile4() throws IOException { 317 | System.out.println("downloadFile as File ("+TESTFILE4+")"); 318 | if (_nc != null) 319 | { 320 | File of= new File(System.getProperty("java.io.tmpdir")+File.separator+TESTFILE4); 321 | if (_nc.downloadFile(TESTFILE4, of.getParent())) 322 | { 323 | assertEquals("Downloaded content size does not match", 0, of.length()); 324 | } 325 | else 326 | { 327 | fail("Downloaded file failed"); 328 | } 329 | if (of.exists()) 330 | { 331 | of.delete(); 332 | } 333 | } 334 | } 335 | 336 | @Test 337 | public void t29_6_testDowloadFile6() throws IOException { 338 | System.out.println("downloadFile as File ("+TESTFILE6+")"); 339 | if (_nc != null) 340 | { 341 | File of= new File(System.getProperty("java.io.tmpdir")+File.separator+TESTFILE6); 342 | if (_nc.downloadFile(TESTFILE6, of.getParent())) 343 | { 344 | File srcFile= new File("src/test/resources/"+TESTFILE6); 345 | assertEquals("Downloaded content size does not match", of.length(), srcFile.length()); 346 | } 347 | else 348 | { 349 | fail("Downloaded file failed"); 350 | } 351 | if (of.exists()) 352 | { 353 | of.delete(); 354 | } 355 | } 356 | } 357 | 358 | @Test 359 | public void t29_6_testDowloadFile7() throws IOException { 360 | System.out.println("downloadFile as File ("+TESTFILE7+")"); 361 | if (_nc != null) 362 | { 363 | File of= new File(System.getProperty("java.io.tmpdir")+File.separator+TESTFILE7); 364 | if (_nc.downloadFile(TESTFILE7, of.getParent())) 365 | { 366 | File srcFile= new File("src/test/resources/"+TESTFILE7); 367 | assertEquals("Downloaded content size does not match", of.length(), srcFile.length()); 368 | } 369 | else 370 | { 371 | fail("Downloaded file failed"); 372 | } 373 | if (of.exists()) 374 | { 375 | of.delete(); 376 | } 377 | } 378 | } 379 | 380 | @Test 381 | public void t31_testRenameFile() { 382 | System.out.println("renameFile 31 ("+TESTFILE1+") to ("+TESTFILE1_RENAMED+")"); 383 | if (_nc != null) 384 | { 385 | _nc.renameFile(TESTFILE1, TESTFILE1_RENAMED, true); 386 | 387 | boolean result = _nc.folderExists(TESTFILE1_RENAMED); 388 | assertTrue(result); 389 | 390 | result = _nc.folderExists(TESTFILE1); 391 | assertFalse(result); 392 | 393 | _nc.renameFile(TESTFILE1_RENAMED, TESTFILE1, true); 394 | 395 | result = _nc.folderExists(TESTFILE1); 396 | assertTrue(result); 397 | } 398 | } 399 | 400 | @Test 401 | public void t99_testRemoveFile() { 402 | System.out.println("removeFile 99 ("+TESTFILE1+")"); 403 | if (_nc != null) 404 | { 405 | _nc.removeFile(TESTFILE1); 406 | } 407 | } 408 | 409 | @Test 410 | public void t99_2_testRemoveFile() { 411 | System.out.println("removeFile 99 2("+TESTFILE2+")"); 412 | if (_nc != null) 413 | { 414 | _nc.removeFile(TESTFILE2); 415 | } 416 | } 417 | 418 | @Test 419 | public void t99_3_testRemoveFile() { 420 | System.out.println("removeFile 99 3("+TESTFILE3+")"); 421 | if (_nc != null) 422 | { 423 | _nc.removeFile(TESTFILE3); 424 | } 425 | } 426 | 427 | @Test 428 | public void t99_4_testRemoveFile() { 429 | System.out.println("removeFile 99 4 ("+TESTFILE4+")"); 430 | if (_nc != null) 431 | { 432 | _nc.removeFile(TESTFILE4); 433 | } 434 | } 435 | 436 | @Test 437 | public void t99_6_testRemoveFile() { 438 | System.out.println("removeFile 99 6 ("+TESTFILE6+")"); 439 | if (_nc != null) 440 | { 441 | _nc.removeFile(TESTFILE6); 442 | } 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/TestFolders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import static org.junit.Assert.assertFalse; 22 | import static org.junit.Assert.assertNotNull; 23 | import static org.junit.Assert.assertTrue; 24 | 25 | import java.util.List; 26 | import static org.aarboard.nextcloud.api.ATestClass.TESTFILE1; 27 | 28 | import org.junit.FixMethodOrder; 29 | import org.junit.Test; 30 | import org.junit.runners.MethodSorters; 31 | 32 | /** 33 | * 34 | * @author a.schild 35 | */ 36 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 37 | public class TestFolders extends ATestClass { 38 | 39 | @Test 40 | public void t21_testCreateFolder() { 41 | System.out.println("createFolder"); 42 | if (_nc != null) 43 | { 44 | 45 | boolean result = _nc.folderExists(TEST_FOLDER1); 46 | if (result) 47 | { 48 | _nc.deleteFolder(TEST_FOLDER1); 49 | } 50 | _nc.createFolder(TEST_FOLDER1); 51 | } 52 | } 53 | 54 | @Test 55 | public void t22_testGetFolders() { 56 | System.out.println("getFolders"); 57 | if (_nc != null) 58 | { 59 | String rootPath = ""; 60 | List result = _nc.getFolders(rootPath); 61 | assertNotNull(result); 62 | assertTrue(result.contains(TEST_FOLDER1)); 63 | } 64 | } 65 | 66 | @Test 67 | public void t222_testCreateFolder() { 68 | System.out.println("createFolder"); 69 | if (_nc != null) 70 | { 71 | 72 | boolean result = _nc.folderExists(TEST_FOLDER2); 73 | if (result) 74 | { 75 | _nc.deleteFolder(TEST_FOLDER2); 76 | } 77 | _nc.createFolder(TEST_FOLDER2); 78 | } 79 | } 80 | 81 | @Test 82 | public void t222_testGetFolders() { 83 | System.out.println("getFolders"); 84 | if (_nc != null) 85 | { 86 | String rootPath = ""; 87 | List result = _nc.getFolders(rootPath); 88 | assertNotNull(result); 89 | assertTrue(result.contains(TEST_FOLDER2)); 90 | } 91 | } 92 | 93 | @Test 94 | public void t23_testFolderExists() { 95 | System.out.println("folderExists"); 96 | if (_nc != null) 97 | { 98 | boolean result = _nc.folderExists(TEST_FOLDER1); 99 | assertTrue(result); 100 | 101 | result = _nc.folderExists("non-existing-folder"); 102 | assertFalse(result); 103 | } 104 | } 105 | 106 | @Test 107 | public void t232_testFolderExists() { 108 | System.out.println("folderExists"); 109 | if (_nc != null) 110 | { 111 | boolean result = _nc.folderExists(TEST_FOLDER2); 112 | assertTrue(result); 113 | 114 | result = _nc.folderExists("non-existing-folder"); 115 | assertFalse(result); 116 | } 117 | } 118 | 119 | @Test 120 | public void t25_testFolderRename() { 121 | System.out.println("folderRename"); 122 | if (_nc != null) 123 | { 124 | _nc.renameFile(TEST_FOLDER1, TEST_FOLDER1_RENAMED, true); 125 | 126 | boolean result = _nc.folderExists(TEST_FOLDER1_RENAMED); 127 | assertTrue(result); 128 | 129 | result = _nc.folderExists(TEST_FOLDER1); 130 | assertFalse(result); 131 | 132 | _nc.renameFile(TEST_FOLDER1_RENAMED, TEST_FOLDER1, true); 133 | 134 | result = _nc.folderExists(TEST_FOLDER1); 135 | assertTrue(result); 136 | } 137 | } 138 | 139 | @Test 140 | public void t30_testDeleteFolder() { 141 | System.out.println("deleteFolder"); 142 | if (_nc != null) 143 | { 144 | _nc.deleteFolder(TEST_FOLDER1); 145 | } 146 | } 147 | 148 | 149 | @Test 150 | public void t40_testList() { 151 | System.out.println("list"); 152 | 153 | if (_nc != null) 154 | { 155 | //prepare 156 | _nc.createFolder(TEST_FOLDER1); 157 | 158 | String rootPath = ""; 159 | List result = _nc.listFolderContent(rootPath); 160 | 161 | //cleanup 162 | _nc.deleteFolder(TEST_FOLDER1); 163 | 164 | assertNotNull(result); 165 | assertTrue(result.contains(TEST_FOLDER1)); 166 | } 167 | } 168 | 169 | @Test 170 | public void t41_testListRecursive() { 171 | System.out.println("list recursive"); 172 | if (_nc != null) 173 | { 174 | //prepare 175 | _nc.createFolder(TEST_FOLDER1); 176 | _nc.createFolder(TEST_FOLDER1+"/"+TEST_FOLDER1+"_sub"); 177 | 178 | String rootPath = ""; 179 | List result = _nc.listFolderContent(TEST_FOLDER1, -1); 180 | 181 | //cleanup 182 | _nc.deleteFolder(TEST_FOLDER1); 183 | 184 | assertNotNull(result); 185 | assertTrue(result.contains(TEST_FOLDER1+"_sub")); 186 | } 187 | } 188 | 189 | @Test 190 | public void t42_testListRecursiveFullPath() { 191 | System.out.println("list recursive"); 192 | if (_nc != null) 193 | { 194 | //prepare 195 | _nc.createFolder(TEST_FOLDER1); 196 | _nc.createFolder(TEST_FOLDER1+"/"+TEST_FOLDER1+"_sub/"); 197 | 198 | String rootPath = ""; 199 | List result = _nc.listFolderContent(TEST_FOLDER1, -1, false, true); 200 | 201 | //cleanup 202 | _nc.deleteFolder(TEST_FOLDER1); 203 | 204 | assertNotNull(result); 205 | assertTrue("Not matching ["+result+"] with expected ["+TEST_FOLDER1+"/"+TEST_FOLDER1+"_sub/"+"]", result.contains(TEST_FOLDER1+"/"+TEST_FOLDER1+"_sub/")); 206 | } 207 | } 208 | 209 | @Test 210 | public void t50_testDownloadFolder() { 211 | System.out.println("testDownloadfolder"); 212 | if (_nc != null) 213 | { 214 | //prepare 215 | File of= new File(System.getProperty("java.io.tmpdir")+File.separator+TEST_FOLDER1); 216 | of.mkdirs(); 217 | boolean result = _nc.folderExists(TEST_FOLDER2); 218 | if (result) 219 | { 220 | _nc.deleteFolder(TEST_FOLDER2); 221 | } 222 | 223 | _nc.createFolder(TEST_FOLDER2); 224 | _nc.createFolder(TEST_FOLDER2+"/"+TEST_FOLDER1); 225 | 226 | try 227 | { 228 | _nc.downloadFolder(TEST_FOLDER2, of.getAbsolutePath()); 229 | //cleanup 230 | _nc.deleteFolder(TEST_FOLDER2); 231 | File of1= new File(System.getProperty("java.io.tmpdir")+File.separator+TEST_FOLDER1+File.separator+TEST_FOLDER2); 232 | 233 | assertTrue("Downloaded folder missing", of1.exists()); 234 | } 235 | catch (IOException ex) 236 | { 237 | assertTrue("Exception in testcase", true); 238 | } 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/TestHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import org.junit.Ignore; 20 | 21 | /** 22 | * 23 | * @author a.schild 24 | */ 25 | @Ignore 26 | public class TestHelper { 27 | private String serverName= null; 28 | private String userName= null; 29 | private String password= null; 30 | private int serverPort= 443; 31 | 32 | public TestHelper() { 33 | serverName= System.getProperty("nextcloud.api.test.servername"); 34 | userName= System.getProperty("nextcloud.api.test.username"); 35 | password= System.getProperty("nextcloud.api.test.password"); 36 | String sPort= System.getProperty("nextcloud.api.test.serverport"); 37 | if (sPort == null || sPort.isEmpty()) 38 | { 39 | serverPort= 443; 40 | } 41 | else 42 | { 43 | serverPort= Integer.parseInt(sPort); 44 | } 45 | } 46 | 47 | /** 48 | * @return the serverName 49 | */ 50 | public String getServerName() { 51 | return serverName; 52 | } 53 | 54 | /** 55 | * @return the userName 56 | */ 57 | public String getUserName() { 58 | return userName; 59 | } 60 | 61 | /** 62 | * @return the password 63 | */ 64 | public String getPassword() { 65 | return password; 66 | } 67 | 68 | /** 69 | * @return the serverPort 70 | */ 71 | public int getServerPort() { 72 | return serverPort; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/TestUserGroupAdmin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import org.aarboard.nextcloud.api.provisioning.User; 20 | import org.aarboard.nextcloud.api.provisioning.UserData; 21 | import org.apache.commons.lang3.StringUtils; 22 | import org.junit.FixMethodOrder; 23 | import org.junit.Test; 24 | import org.junit.runners.MethodSorters; 25 | 26 | import java.util.Arrays; 27 | import java.util.Collection; 28 | import java.util.List; 29 | 30 | import static org.junit.Assert.*; 31 | 32 | /** 33 | * 34 | * @author a.schild 35 | */ 36 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 37 | public class TestUserGroupAdmin extends ATestClass { 38 | 39 | @Test 40 | public void t01_testCreateGroup() { 41 | System.out.println("createGroup"); 42 | if (_nc != null) { 43 | String groupId = TESTGROUP; 44 | boolean result = _nc.createGroup(groupId); 45 | assertTrue(result); 46 | } 47 | } 48 | 49 | @Test 50 | public void t02_testGetGroups_0args() { 51 | System.out.println("getGroups"); 52 | if (_nc != null) { 53 | Collection result = _nc.getGroups(); 54 | assertNotNull(result); 55 | assertTrue(result.contains(TESTGROUP)); 56 | } 57 | } 58 | 59 | @Test 60 | public void t03_testGetGroups_3args() { 61 | System.out.println("getGroups"); 62 | if (_nc != null) { 63 | String search = TESTGROUP; 64 | int limit = 1; 65 | int offset = -1; 66 | Collection result = _nc.getGroups(search, limit, offset); 67 | assertNotNull(result); 68 | assertTrue(result.contains(TESTGROUP)); 69 | } 70 | } 71 | 72 | @Test 73 | public void t04_00_testCreateUser() { 74 | System.out.println("createUser"); 75 | if (_nc != null) { 76 | boolean result = _nc.createUser(TESTUSER, "aBcDeFg123456"); 77 | assertTrue(result); 78 | } 79 | } 80 | 81 | @Test 82 | public void t04_01_testCreateUserFull() { 83 | System.out.println("createUser"); 84 | if (_nc != null) { 85 | boolean result = _nc.createUser(TESTUSER+"Full", "aBcDeFg123456", 86 | "Full Test User", "full@test.org", "5368709120000B", "de", Arrays.asList(TESTGROUP)); 87 | assertTrue(result); 88 | } 89 | } 90 | 91 | @Test 92 | public void t04_02_testCreateUserWithInvalidChar() { 93 | System.out.println("createUserWithInvalidChar"); 94 | if (_nc != null) { 95 | boolean result = _nc.createUser(TESTUSER_WITH_INVALID_CHAR, "aBcDeFg123456"); 96 | assertTrue(result); 97 | } 98 | } 99 | 100 | @Test 101 | public void t05_00_testGetUsers() { 102 | System.out.println("getUsers"); 103 | if (_nc != null) { 104 | Collection result = _nc.getUsers(); 105 | assertNotNull(result); 106 | assertTrue(result.contains(TESTUSER)); 107 | } 108 | } 109 | 110 | @Test 111 | public void t05_01_testGetUsersDetails() { 112 | System.out.println("getUsersDetails"); 113 | if (_nc != null) 114 | { 115 | Collection result = _nc.getUsersDetails(); 116 | assertNotNull(result); 117 | assertTrue(result.stream().anyMatch(user -> TESTUSER.equals(user.getId()))); 118 | assertTrue(result.stream().anyMatch(user -> TESTUSER_WITH_INVALID_CHAR.equals(user.getId()))); 119 | } 120 | } 121 | 122 | @Test 123 | public void t06_00_testGetUsers_3args() { 124 | System.out.println("getUsers"); 125 | if (_nc != null) { 126 | String search = TESTUSER; 127 | int limit = 1; 128 | int offset = -1; 129 | Collection result = _nc.getUsers(search, limit, offset); 130 | assertNotNull(result); 131 | assertTrue(result.contains(TESTUSER)); 132 | } 133 | } 134 | 135 | @Test 136 | public void t06_01_testGetUsersDetails_3args() { 137 | System.out.println("getUsersDetails"); 138 | if (_nc != null) { 139 | int limit = 1; 140 | int offset = -1; 141 | Collection result1 = _nc.getUsersDetails(TESTUSER, limit, offset); 142 | assertNotNull(result1); 143 | assertTrue(result1.stream().anyMatch(user -> TESTUSER.equals(user.getId()))); 144 | Collection result2 = _nc.getUsersDetails(TESTUSER_WITH_INVALID_CHAR, limit, offset); 145 | assertNotNull(result2); 146 | assertTrue(result2.stream().anyMatch(user -> TESTUSER_WITH_INVALID_CHAR.equals(user.getId()))); 147 | } 148 | } 149 | 150 | @Test 151 | public void t07_testEditUser() { 152 | System.out.println("editUser"); 153 | if (_nc != null) { 154 | boolean result = _nc.editUser(TESTUSER, UserData.TWITTER, "test"); 155 | assertTrue(result); 156 | } 157 | } 158 | 159 | @Test 160 | public void t08_testDisableUser() { 161 | System.out.println("disableUser"); 162 | if (_nc != null) { 163 | boolean result = _nc.disableUser(TESTUSER); 164 | assertTrue(result); 165 | } 166 | } 167 | 168 | @Test 169 | public void t09_00_testGetUser() { 170 | System.out.println("getUser"); 171 | if (_nc != null) { 172 | User result = _nc.getUser(TESTUSER); 173 | assertNotNull(result); 174 | assertEquals(TESTUSER, result.getId()); 175 | assertEquals("test", result.getTwitter()); 176 | assertFalse(result.isEnabled()); 177 | } 178 | } 179 | 180 | @Test 181 | public void t09_01_testGetCurrentUser() { 182 | System.out.println("getCurrentUser"); 183 | if (_nc != null) { 184 | User user = _nc.getCurrentUser(); 185 | assertNotNull(user); 186 | } 187 | } 188 | 189 | @Test 190 | public void t10_testEnableUser() { 191 | System.out.println("enableUser"); 192 | if (_nc != null) { 193 | boolean result = _nc.enableUser(TESTUSER); 194 | assertTrue(result); 195 | } 196 | } 197 | 198 | @Test 199 | public void t11_testAddUserToGroup() { 200 | System.out.println("addUserToGroup"); 201 | if (_nc != null) { 202 | boolean result = _nc.addUserToGroup(TESTUSER, TESTGROUP); 203 | assertTrue(result); 204 | } 205 | } 206 | 207 | @Test 208 | public void t12_testGetGroupsOfUser() { 209 | System.out.println("getGroupsOfUser"); 210 | if (_nc != null) { 211 | List result = _nc.getGroupsOfUser(TESTUSER); 212 | assertNotNull(result); 213 | assertTrue(result.contains(TESTGROUP)); 214 | } 215 | } 216 | 217 | @Test 218 | public void t13_00_testGetMembersOfGroup() { 219 | System.out.println("getMembersOfGroup"); 220 | if (_nc != null) { 221 | List result = _nc.getMembersOfGroup(TESTGROUP); 222 | assertNotNull(result); 223 | assertTrue(result.contains(TESTUSER)); 224 | } 225 | } 226 | 227 | @Test 228 | public void t13_01_testGetMembersDetailsOfGroup() { 229 | System.out.println("getMembersDetailsOfGroup"); 230 | if (_nc != null) { 231 | List result = _nc.getMembersDetailsOfGroup(TESTGROUP); 232 | assertNotNull(result); 233 | assertTrue(result.stream().anyMatch(user -> StringUtils.equals(user.getId(), TESTUSER))); 234 | } 235 | } 236 | 237 | @Test 238 | public void t14_testPromoteToSubadmin() { 239 | System.out.println("promoteToSubadmin"); 240 | if (_nc != null) { 241 | boolean result = _nc.promoteToSubadmin(TESTUSER, TESTGROUP); 242 | assertTrue(result); 243 | } 244 | } 245 | 246 | @Test 247 | public void t15_testGetSubadminGroupsOfUser() { 248 | System.out.println("getSubadminGroupsOfUser"); 249 | if (_nc != null) { 250 | List result = _nc.getSubadminGroupsOfUser(TESTUSER); 251 | assertNotNull(result); 252 | assertTrue(result.contains(TESTGROUP)); 253 | } 254 | } 255 | 256 | @Test 257 | public void t16_testGetSubadminsOfGroup() { 258 | System.out.println("getSubadminsOfGroup"); 259 | if (_nc != null) { 260 | List result = _nc.getSubadminsOfGroup(TESTGROUP); 261 | assertNotNull(result); 262 | assertTrue(result.contains(TESTUSER)); 263 | } 264 | } 265 | 266 | @Test 267 | public void t17_testDemoteSubadmin() { 268 | System.out.println("demoteSubadmin"); 269 | if (_nc != null) { 270 | boolean result = _nc.demoteSubadmin(TESTUSER, TESTGROUP); 271 | assertTrue(result); 272 | } 273 | } 274 | 275 | @Test 276 | public void t18_testRemoveUserFromGroup() { 277 | System.out.println("removeUserFromGroup"); 278 | if (_nc != null) { 279 | boolean result = _nc.removeUserFromGroup(TESTUSER, TESTGROUP); 280 | assertTrue(result); 281 | } 282 | } 283 | 284 | @Test 285 | public void t19_00_testDeleteUser() { 286 | System.out.println("deleteUser"); 287 | if (_nc != null) { 288 | boolean result = _nc.deleteUser(TESTUSER); 289 | assertTrue(result); 290 | } 291 | } 292 | 293 | @Test 294 | public void t19_01_testDeleteUserFull() { 295 | System.out.println("deleteUserFull"); 296 | if (_nc != null) { 297 | boolean result = _nc.deleteUser(TESTUSER+"Full"); 298 | assertTrue(result); 299 | } 300 | } 301 | 302 | @Test 303 | public void t19_02_testDeleteUserWithInvalidChar() { 304 | System.out.println("deleteUserWithInvalidChar"); 305 | if (_nc != null) { 306 | boolean result = _nc.deleteUser(TESTUSER_WITH_INVALID_CHAR); 307 | assertTrue(result); 308 | } 309 | } 310 | 311 | @Test 312 | public void t20_testDeleteGroup() { 313 | System.out.println("deleteGroup"); 314 | if (_nc != null) { 315 | String groupId = TESTGROUP; 316 | boolean result = _nc.deleteGroup(groupId); 317 | assertTrue(result); 318 | } 319 | } 320 | 321 | } 322 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/WebDavPathResolverBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 scm11361 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api; 18 | 19 | import org.aarboard.nextcloud.api.webdav.pathresolver.NextcloudVersion; 20 | import org.aarboard.nextcloud.api.webdav.pathresolver.WebDavPathResolverBuilder; 21 | import org.junit.Assert; 22 | import static org.junit.Assert.assertNotNull; 23 | import org.junit.Test; 24 | 25 | public class WebDavPathResolverBuilderTest 26 | { 27 | 28 | @Test 29 | public void testBuilder_version_1() 30 | { 31 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.VERSION).build().getWebDavPath(); 32 | 33 | assertNotNull(result); 34 | 35 | Assert.assertEquals("/status.php", result); 36 | 37 | } 38 | 39 | @Test 40 | public void testBuilder_version_2() 41 | { 42 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.VERSION) 43 | .withBasePathSuffix("x").withUserName("x").build().getWebDavPath(); 44 | 45 | assertNotNull(result); 46 | 47 | Assert.assertEquals("/status.php", result); 48 | 49 | } 50 | 51 | @Test 52 | public void testBuilder_version_3() 53 | { 54 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.VERSION) 55 | .withBasePathPrefix("nextcloud").withBasePathSuffix("x") 56 | .withUserName("x").build().getWebDavPath(); 57 | 58 | assertNotNull(result); 59 | 60 | Assert.assertEquals("/nextcloud/status.php", result); 61 | 62 | } 63 | 64 | @Test 65 | public void testBuilder_version_4() 66 | { 67 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.VERSION) 68 | .withBasePath("/mypath/other_status.php") 69 | .withBasePathPrefix("nextcloud") 70 | .withBasePathSuffix("x") 71 | .withUserName("x").build().getWebDavPath(); 72 | 73 | assertNotNull(result); 74 | 75 | Assert.assertEquals("/nextcloud/mypath/other_status.php", result); 76 | 77 | } 78 | 79 | @Test 80 | public void testBuilder_folder_1() 81 | { 82 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.FILES) 83 | .withBasePathPrefix("nextcloud").withBasePathSuffix("suf") 84 | .withUserName("user").build().getWebDavPath(); 85 | 86 | assertNotNull(result); 87 | 88 | Assert.assertEquals("/nextcloud/remote.php/dav/suf/user/", result); 89 | 90 | } 91 | 92 | @Test 93 | public void testBuilder_folder_2() 94 | { 95 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.FILES) 96 | .ofVersion(NextcloudVersion.get("14.0.1")) 97 | .withBasePathPrefix("nextcloud").withBasePathSuffix("suf").withUserName("user").build().getWebDavPath(); 98 | 99 | assertNotNull(result); 100 | 101 | Assert.assertEquals("/nextcloud/remote.php/webdav/", result); 102 | 103 | } 104 | 105 | @Test 106 | public void testBuilder_folder_3() 107 | { 108 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.FILES) 109 | .ofVersion(NextcloudVersion.get("20.0.1")) 110 | .withBasePathPrefix("nextcloud") 111 | .withUserName("user").build().getWebDavPath(); 112 | 113 | assertNotNull(result); 114 | 115 | Assert.assertEquals("/nextcloud/remote.php/dav/files/user/", result); 116 | 117 | } 118 | 119 | @Test 120 | public void testBuilder_folder_4() 121 | { 122 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.CALDAV) 123 | .withBasePathSuffix("calendar") 124 | .ofVersion(NextcloudVersion.get("20.0.1")) 125 | .withBasePathPrefix("nextcloud") 126 | .withUserName("angus").build().getWebDavPath(); 127 | 128 | assertNotNull(result); 129 | Assert.assertEquals("/nextcloud/remote.php/dav/calendar/angus/", result); 130 | 131 | } 132 | 133 | @Test 134 | public void testBuilder_folder_5() 135 | { 136 | String result = WebDavPathResolverBuilder.get(WebDavPathResolverBuilder.TYPE.CALDAV) 137 | .ofVersion(NextcloudVersion.get("20.0.1")) 138 | .withBasePathPrefix("nextcloud") 139 | .withUserName("angus").build().getWebDavPath(); 140 | 141 | assertNotNull(result); 142 | Assert.assertEquals("/nextcloud/remote.php/dav/calendars/angus/", result); 143 | 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/org/aarboard/nextcloud/api/filesharing/FilesharingConnectorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 a.schild 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.aarboard.nextcloud.api.filesharing; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertNotNull; 22 | import static org.junit.Assert.assertTrue; 23 | import static org.junit.Assert.fail; 24 | 25 | import java.time.LocalDate; 26 | import java.util.Collection; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | import java.util.Optional; 30 | 31 | import org.aarboard.nextcloud.api.AuthenticationConfig; 32 | import org.aarboard.nextcloud.api.NextcloudConnector; 33 | import org.aarboard.nextcloud.api.ServerConfig; 34 | import org.aarboard.nextcloud.api.TestHelper; 35 | import org.aarboard.nextcloud.api.exception.NextcloudOperationFailedException; 36 | import org.aarboard.nextcloud.api.filesharing.SharePermissions.SingleRight; 37 | import org.aarboard.nextcloud.api.provisioning.ShareData; 38 | import org.junit.AfterClass; 39 | import org.junit.BeforeClass; 40 | import org.junit.FixMethodOrder; 41 | import org.junit.Test; 42 | import org.junit.runners.MethodSorters; 43 | 44 | /** 45 | * 46 | * @author a.schild 47 | */ 48 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 49 | public class FilesharingConnectorTest { 50 | private static final String TEST_FOLDER = "/sharing-test-folder"; 51 | private static final String TEST_FOLDER2 = "/sharing-test-folder2"; 52 | private static final String TEST_FOLDER3 = "/sharing-test-folder3 + sign \u32A2"; 53 | private static final String TESTUSER = "sharing-testuser"; 54 | private static final String TESTUSER_EMAIL = "sharing@example.com"; 55 | 56 | private static String serverName = null; 57 | private static String userName = null; 58 | private static String password = null; 59 | private static int serverPort= 443; 60 | 61 | private static ServerConfig _sc = null; 62 | private static NextcloudConnector _nc = null; 63 | 64 | @BeforeClass 65 | public static void setUp() { 66 | TestHelper th= new TestHelper(); 67 | serverName= th.getServerName(); 68 | userName= th.getUserName(); 69 | password= th.getPassword(); 70 | serverPort= th.getServerPort(); 71 | if (serverName != null) 72 | { 73 | _sc= new ServerConfig(serverName, serverPort == 443, serverPort, new AuthenticationConfig(userName, password)); 74 | _nc = new NextcloudConnector(serverName, serverPort == 443, serverPort, userName, password); 75 | _nc.createFolder(TEST_FOLDER); 76 | _nc.createFolder(TEST_FOLDER2); 77 | _nc.createFolder(TEST_FOLDER3); 78 | _nc.createUser(TESTUSER, "aBcDeFg123456"); 79 | } 80 | } 81 | 82 | @AfterClass 83 | public static void tearDown() { 84 | if (serverName != null) 85 | { 86 | _nc.deleteFolder(TEST_FOLDER); 87 | _nc.deleteFolder(TEST_FOLDER2); 88 | _nc.deleteFolder(TEST_FOLDER3); 89 | _nc.deleteUser(TESTUSER); 90 | } 91 | } 92 | 93 | @Test 94 | public void t01_testDoShare() { 95 | System.out.println("shareFolder"); 96 | if (_sc != null) 97 | { 98 | FilesharingConnector instance = new FilesharingConnector(_sc); 99 | Share result = instance.doShare(TEST_FOLDER, ShareType.USER, TESTUSER, null, null, null); 100 | assertNotNull(result); 101 | } 102 | } 103 | 104 | @Test 105 | public void t02_testDoShareEmail() { 106 | System.out.println("shareFolderEmail"); 107 | if (_sc != null) 108 | { 109 | FilesharingConnector instance = new FilesharingConnector(_sc); 110 | Share result = instance.doShare(TEST_FOLDER, ShareType.EMAIL, TESTUSER_EMAIL, null, null, null); 111 | assertNotNull(result); 112 | } 113 | } 114 | 115 | @Test 116 | public void t03_testDoShare2() { 117 | System.out.println("shareFolder2"); 118 | if (_sc != null) 119 | { 120 | FilesharingConnector instance = new FilesharingConnector(_sc); 121 | SharePermissions permissions = new SharePermissions(SingleRight.READ); 122 | Share result = instance.doShare(TEST_FOLDER2,ShareType.PUBLIC_LINK,"",true,"1234-myWebDav",permissions); 123 | assertNotNull(result); 124 | } 125 | } 126 | 127 | @Test 128 | public void t03_testDoShare3() { 129 | System.out.println("shareFolder3"); 130 | if (_sc != null) 131 | { 132 | FilesharingConnector instance = new FilesharingConnector(_sc); 133 | SharePermissions permissions = new SharePermissions(SingleRight.READ); 134 | Share result = instance.doShare(TEST_FOLDER3,ShareType.PUBLIC_LINK,"",true,"1234-myWebDav",permissions); 135 | assertNotNull(result); 136 | } 137 | } 138 | 139 | 140 | @Test 141 | public void t03_testGetShares() { 142 | System.out.println("getShares"); 143 | if (_sc != null) 144 | { 145 | FilesharingConnector instance = new FilesharingConnector(_sc); 146 | Collection result = instance.getShares(); 147 | assertNotNull(result); 148 | assertTrue(result.stream().anyMatch(s -> TEST_FOLDER.equals(s.getPath()) && TESTUSER.equals(s.getShareWithId()))); 149 | } 150 | } 151 | 152 | @Test 153 | public void t04_testEditShare() { 154 | System.out.println("editShare"); 155 | if (_sc != null) 156 | { 157 | FilesharingConnector instance = new FilesharingConnector(_sc); 158 | boolean result = instance.editShare(findShare(instance).get().getId(), ShareData.EXPIREDATE, "9999-01-01"); 159 | assertTrue(result); 160 | } 161 | } 162 | 163 | @Test 164 | public void t05_testGetSharesOfPath() { 165 | System.out.println("getSharesOfPath"); 166 | if (_sc != null) 167 | { 168 | FilesharingConnector instance = new FilesharingConnector(_sc); 169 | Collection result = instance.getShares(TEST_FOLDER, false, false); 170 | assertNotNull(result); 171 | 172 | Optional share = findShare(instance); 173 | assertTrue(share.isPresent()); 174 | assertEquals(LocalDate.of(9999, 1, 1), share.get().getExpiration()); 175 | 176 | result = instance.getShares(TEST_FOLDER, true, false); 177 | assertNotNull(result); 178 | 179 | result = instance.getShares(TEST_FOLDER, false, true); 180 | assertNotNull(result); 181 | 182 | result = instance.getShares(TEST_FOLDER, true, true); 183 | assertNotNull(result); 184 | } 185 | } 186 | 187 | private Optional findShare(FilesharingConnector instance) { 188 | return instance.getShares(TEST_FOLDER, false, false).stream() 189 | .filter(s -> TEST_FOLDER.equals(s.getPath()) && TESTUSER.equals(s.getShareWithId())).findFirst(); 190 | } 191 | 192 | @Test 193 | public void t06_testEditShare_Map() { 194 | System.out.println("editShare"); 195 | if (_sc != null) 196 | { 197 | FilesharingConnector instance = new FilesharingConnector(_sc); 198 | Map parameters = new HashMap<>(); 199 | parameters.put(ShareData.EXPIREDATE, "9999-02-02"); 200 | parameters.put(ShareData.PERMISSIONS, new SharePermissions(SingleRight.READ, SingleRight.UPDATE).toString()); 201 | boolean result = instance.editShare(findShare(instance).get().getId(), parameters); 202 | assertTrue(result); 203 | } 204 | } 205 | 206 | @Test 207 | public void t07_testGetShareInfo() { 208 | System.out.println("getShareInfo"); 209 | if (_sc != null) 210 | { 211 | FilesharingConnector instance = new FilesharingConnector(_sc); 212 | Share result = instance.getShareInfo(findShare(instance).get().getId()); 213 | assertNotNull(result); 214 | assertEquals(TEST_FOLDER, result.getPath()); 215 | assertEquals(TESTUSER, result.getShareWithId()); 216 | assertEquals(LocalDate.of(9999, 2, 2), result.getExpiration()); 217 | assertTrue(result.getSharePermissions().hasRight(SingleRight.READ)); 218 | assertTrue(result.getSharePermissions().hasRight(SingleRight.UPDATE)); 219 | assertFalse(result.getSharePermissions().hasRight(SingleRight.CREATE)); 220 | assertFalse(result.getSharePermissions().hasRight(SingleRight.DELETE)); 221 | assertFalse(result.getSharePermissions().hasRight(SingleRight.SHARE)); 222 | 223 | try { 224 | instance.getShareInfo(89989899); 225 | fail("NextcloudOperationFailedException should be thrown!"); 226 | } catch(NextcloudOperationFailedException ex) { 227 | } 228 | } 229 | } 230 | 231 | @Test 232 | public void t08_testDeleteShare() { 233 | System.out.println("deleteShare"); 234 | if (_sc != null) 235 | { 236 | FilesharingConnector instance = new FilesharingConnector(_sc); 237 | boolean result = instance.deleteShare(findShare(instance).get().getId()); 238 | assertTrue(result); 239 | } 240 | } 241 | 242 | } 243 | -------------------------------------------------------------------------------- /src/test/resources/test6.txt: -------------------------------------------------------------------------------- 1 | 1234567890 2 | test6.txt file -------------------------------------------------------------------------------- /src/test/testdata/test5.txt: -------------------------------------------------------------------------------- 1 | Test file 1 2 | File test1.txt 3 | --------------------------------------------------------------------------------