2525import java .util .List ;
2626import java .util .stream .IntStream ;
2727
28+ import static org .cryptomator .cloudaccess .api .CloudItemType .FILE ;
29+ import static org .cryptomator .cloudaccess .api .CloudItemType .FOLDER ;
30+
2831public class WebDavClient {
2932
30- private static final Comparator <PropfindEntryData > ASCENDING_BY_DEPTH = Comparator .comparingInt (PropfindEntryData ::getDepth );
33+ private static final Comparator <PropfindEntryData > ASCENDING_BY_DEPTH = Comparator .comparingLong (PropfindEntryData ::getDepth );
3134
3235 private final WebDavCompatibleHttpClient httpClient ;
3336 private final URL baseUrl ;
@@ -39,30 +42,30 @@ public class WebDavClient {
3942 }
4043
4144 CloudItemList list (final CloudPath folder ) throws CloudProviderException {
42- try (final var response = executePropfindRequest (folder , PROPFIND_DEPTH .ONE )) {
45+ try (final var response = executePropfindRequest (folder , PropfindDepth .ONE )) {
4346 checkExecutionSucceeded (response .code ());
4447
4548 final var nodes = getEntriesFromResponse (response );
4649
47- return processDirList (nodes );
50+ return processDirList (nodes , folder );
4851 } catch (IOException | SAXException e ) {
4952 throw new CloudProviderException (e );
5053 }
5154 }
5255
5356 CloudItemMetadata itemMetadata (final CloudPath path ) throws CloudProviderException {
54- try (final var response = executePropfindRequest (path , PROPFIND_DEPTH .ZERO )) {
57+ try (final var response = executePropfindRequest (path , PropfindDepth .ZERO )) {
5558 checkExecutionSucceeded (response .code ());
5659
5760 final var nodes = getEntriesFromResponse (response );
5861
59- return processGet (nodes );
62+ return processGet (nodes , path );
6063 } catch (IOException | SAXException e ) {
6164 throw new CloudProviderException (e );
6265 }
6366 }
6467
65- private Response executePropfindRequest (final CloudPath path , final PROPFIND_DEPTH propfind_depth ) throws IOException {
68+ private Response executePropfindRequest (final CloudPath path , final PropfindDepth propfindDepth ) throws IOException {
6669 final var body = "<d:propfind xmlns:d=\" DAV:\" >\n " //
6770 + "<d:prop>\n " //
6871 + "<d:resourcetype />\n " //
@@ -74,7 +77,7 @@ private Response executePropfindRequest(final CloudPath path, final PROPFIND_DEP
7477 final var builder = new Request .Builder () //
7578 .method ("PROPFIND" , RequestBody .create (body , MediaType .parse (body ))) //
7679 .url (absoluteURLFrom (path )) //
77- .header ("DEPTH " , propfind_depth .value ) //
80+ .header ("Depth " , propfindDepth .value ) //
7881 .header ("Content-Type" , "text/xml" );
7982
8083 return httpClient .execute (builder );
@@ -86,12 +89,12 @@ private List<PropfindEntryData> getEntriesFromResponse(final Response response)
8689 }
8790 }
8891
89- private CloudItemMetadata processGet (final List <PropfindEntryData > entryData ) {
92+ private CloudItemMetadata processGet (final List <PropfindEntryData > entryData , final CloudPath path ) {
9093 entryData .sort (ASCENDING_BY_DEPTH );
91- return entryData .size () >= 1 ? entryData .get (0 ). toCloudItem ( ) : null ;
94+ return entryData .size () >= 1 ? toCloudItem ( entryData .get (0 ), path ) : null ;
9295 }
9396
94- private CloudItemList processDirList (final List <PropfindEntryData > entryData ) {
97+ private CloudItemList processDirList (final List <PropfindEntryData > entryData , final CloudPath folder ) {
9598 var result = new CloudItemList (new ArrayList <>());
9699
97100 if (entryData .isEmpty ()) {
@@ -103,11 +106,19 @@ private CloudItemList processDirList(final List<PropfindEntryData> entryData) {
103106 // because it's depth is 1 smaller than the depth
104107 // ot the other entries, thus we skip the first entry
105108 for (PropfindEntryData childEntry : entryData .subList (1 , entryData .size ())) {
106- result = result .add (List .of (childEntry . toCloudItem ( )));
109+ result = result .add (List .of (toCloudItem ( childEntry , folder . resolve ( childEntry . getName ()) )));
107110 }
108111 return result ;
109112 }
110113
114+ private CloudItemMetadata toCloudItem (final PropfindEntryData data , final CloudPath path ) {
115+ if (data .isCollection ()) {
116+ return new CloudItemMetadata (data .getName (), path , FOLDER );
117+ } else {
118+ return new CloudItemMetadata (data .getName (), path , FILE , data .getLastModified (), data .getSize ());
119+ }
120+ }
121+
111122 CloudPath move (final CloudPath from , final CloudPath to , boolean replace ) throws CloudProviderException {
112123 final var builder = new Request .Builder () //
113124 .method ("MOVE" , null ) //
@@ -142,7 +153,7 @@ InputStream read(final CloudPath path, final ProgressListener progressListener)
142153
143154 InputStream read (final CloudPath path , final long offset , final long count , final ProgressListener progressListener ) throws CloudProviderException {
144155 final var getRequest = new Request .Builder () //
145- .header ("Range" , String .format ("bytes=%d-%d" , offset , offset + count - 1 ))
156+ .header ("Range" , String .format ("bytes=%d-%d" , offset , offset + count - 1 )) //
146157 .get () //
147158 .url (absoluteURLFrom (path ));
148159 return read (getRequest , progressListener );
@@ -156,7 +167,7 @@ private InputStream read(final Request.Builder getRequest, final ProgressListene
156167 final var countingBody = new ProgressResponseWrapper (response .body (), progressListener );
157168
158169 final int UNSATISFIABLE_RANGE = 416 ;
159- if (response .code () == UNSATISFIABLE_RANGE ) {
170+ if (response .code () == UNSATISFIABLE_RANGE ) {
160171 return new ByteArrayInputStream (new byte [0 ]);
161172 }
162173
@@ -178,8 +189,8 @@ CloudItemMetadata write(final CloudPath file, final boolean replace, final Input
178189 }
179190
180191 final var countingBody = new ProgressRequestWrapper (InputStreamRequestBody .from (data , size ), progressListener );
181- final var requestBuilder = new Request .Builder ()
182- .url (absoluteURLFrom (file ))
192+ final var requestBuilder = new Request .Builder () //
193+ .url (absoluteURLFrom (file )) //
183194 .put (countingBody );
184195
185196 try (final var response = httpClient .execute (requestBuilder )) {
@@ -199,7 +210,7 @@ private boolean exists(CloudPath path) throws CloudProviderException {
199210 }
200211
201212 CloudPath createFolder (final CloudPath path ) throws CloudProviderException {
202- if (exists (path )) {
213+ if (exists (path )) {
203214 throw new AlreadyExistsException (String .format ("Folder %s already exists" , path .toString ()));
204215 }
205216
@@ -228,8 +239,8 @@ void delete(final CloudPath path) throws CloudProviderException {
228239 }
229240
230241 void checkServerCompatibility () throws ServerNotWebdavCompatibleException {
231- final var optionsRequest = new Request .Builder ()
232- .method ("OPTIONS" , null )
242+ final var optionsRequest = new Request .Builder () //
243+ .method ("OPTIONS" , null ) //
233244 .url (baseUrl );
234245
235246 try (final var response = httpClient .execute (optionsRequest )) {
@@ -282,19 +293,20 @@ URL absoluteURLFrom(final CloudPath relativePath) {
282293 }
283294 }
284295
285- private enum PROPFIND_DEPTH {
286- ZERO ("0" ),
287- ONE ("1" ),
296+ private enum PropfindDepth {
297+ ZERO ("0" ), //
298+ ONE ("1" ), //
288299 INFINITY ("infinity" );
289300
290301 private final String value ;
291302
292- PROPFIND_DEPTH (final String value ) {
303+ PropfindDepth (final String value ) {
293304 this .value = value ;
294305 }
295306 }
296307
297308 static class WebDavAuthenticator {
309+
298310 static WebDavClient createAuthenticatedWebDavClient (final WebDavCredential webDavCredential ) throws ServerNotWebdavCompatibleException , UnauthorizedException {
299311 final var webDavClient = new WebDavClient (new WebDavCompatibleHttpClient (webDavCredential ), webDavCredential );
300312
0 commit comments