66import okhttp3 .Response ;
77import org .cryptomator .cloudaccess .api .CloudItemList ;
88import org .cryptomator .cloudaccess .api .CloudItemMetadata ;
9+ import org .cryptomator .cloudaccess .api .CloudItemType ;
910import org .cryptomator .cloudaccess .api .CloudPath ;
1011import org .cryptomator .cloudaccess .api .ProgressListener ;
1112import org .cryptomator .cloudaccess .api .exceptions .AlreadyExistsException ;
2728
2829public class WebDavClient {
2930
30- private static final Comparator <PropfindEntryData > ASCENDING_BY_DEPTH = Comparator .comparingInt (PropfindEntryData ::getDepth );
31+ private static final Comparator <PropfindEntryData > ASCENDING_BY_DEPTH = Comparator .comparingLong (PropfindEntryData ::getDepth );
3132
3233 private final WebDavCompatibleHttpClient httpClient ;
3334 private final URL baseUrl ;
@@ -39,30 +40,30 @@ public class WebDavClient {
3940 }
4041
4142 CloudItemList list (final CloudPath folder ) throws CloudProviderException {
42- try (final var response = executePropfindRequest (folder , PROPFIND_DEPTH .ONE )) {
43+ try (final var response = executePropfindRequest (folder , PropfindDepth .ONE )) {
4344 checkExecutionSucceeded (response .code ());
4445
4546 final var nodes = getEntriesFromResponse (response );
4647
47- return processDirList (nodes );
48+ return processDirList (nodes , folder );
4849 } catch (IOException | SAXException e ) {
4950 throw new CloudProviderException (e );
5051 }
5152 }
5253
5354 CloudItemMetadata itemMetadata (final CloudPath path ) throws CloudProviderException {
54- try (final var response = executePropfindRequest (path , PROPFIND_DEPTH .ZERO )) {
55+ try (final var response = executePropfindRequest (path , PropfindDepth .ZERO )) {
5556 checkExecutionSucceeded (response .code ());
5657
5758 final var nodes = getEntriesFromResponse (response );
5859
59- return processGet (nodes );
60+ return processGet (nodes , path );
6061 } catch (IOException | SAXException e ) {
6162 throw new CloudProviderException (e );
6263 }
6364 }
6465
65- private Response executePropfindRequest (final CloudPath path , final PROPFIND_DEPTH propfind_depth ) throws IOException {
66+ private Response executePropfindRequest (final CloudPath path , final PropfindDepth propfindDepth ) throws IOException {
6667 final var body = "<d:propfind xmlns:d=\" DAV:\" >\n " //
6768 + "<d:prop>\n " //
6869 + "<d:resourcetype />\n " //
@@ -74,7 +75,7 @@ private Response executePropfindRequest(final CloudPath path, final PROPFIND_DEP
7475 final var builder = new Request .Builder () //
7576 .method ("PROPFIND" , RequestBody .create (body , MediaType .parse (body ))) //
7677 .url (absoluteURLFrom (path )) //
77- .header ("DEPTH " , propfind_depth .value ) //
78+ .header ("Depth " , propfindDepth .value ) //
7879 .header ("Content-Type" , "text/xml" );
7980
8081 return httpClient .execute (builder );
@@ -86,12 +87,12 @@ private List<PropfindEntryData> getEntriesFromResponse(final Response response)
8687 }
8788 }
8889
89- private CloudItemMetadata processGet (final List <PropfindEntryData > entryData ) {
90+ private CloudItemMetadata processGet (final List <PropfindEntryData > entryData , final CloudPath path ) {
9091 entryData .sort (ASCENDING_BY_DEPTH );
91- return entryData .size () >= 1 ? entryData .get (0 ). toCloudItem ( ) : null ;
92+ return entryData .size () >= 1 ? toCloudItem ( entryData .get (0 ), path ) : null ;
9293 }
9394
94- private CloudItemList processDirList (final List <PropfindEntryData > entryData ) {
95+ private CloudItemList processDirList (final List <PropfindEntryData > entryData , final CloudPath folder ) {
9596 var result = new CloudItemList (new ArrayList <>());
9697
9798 if (entryData .isEmpty ()) {
@@ -103,11 +104,19 @@ private CloudItemList processDirList(final List<PropfindEntryData> entryData) {
103104 // because it's depth is 1 smaller than the depth
104105 // ot the other entries, thus we skip the first entry
105106 for (PropfindEntryData childEntry : entryData .subList (1 , entryData .size ())) {
106- result = result .add (List .of (childEntry . toCloudItem ( )));
107+ result = result .add (List .of (toCloudItem ( childEntry , folder . resolve ( childEntry . getName ()) )));
107108 }
108109 return result ;
109110 }
110111
112+ private CloudItemMetadata toCloudItem (final PropfindEntryData data , final CloudPath path ) {
113+ if (data .isCollection ()) {
114+ return new CloudItemMetadata (data .getName (), path , CloudItemType .FOLDER );
115+ } else {
116+ return new CloudItemMetadata (data .getName (), path , CloudItemType .FILE , data .getLastModified (), data .getSize ());
117+ }
118+ }
119+
111120 CloudPath move (final CloudPath from , final CloudPath to , boolean replace ) throws CloudProviderException {
112121 final var builder = new Request .Builder () //
113122 .method ("MOVE" , null ) //
@@ -142,7 +151,7 @@ InputStream read(final CloudPath path, final ProgressListener progressListener)
142151
143152 InputStream read (final CloudPath path , final long offset , final long count , final ProgressListener progressListener ) throws CloudProviderException {
144153 final var getRequest = new Request .Builder () //
145- .header ("Range" , String .format ("bytes=%d-%d" , offset , offset + count - 1 ))
154+ .header ("Range" , String .format ("bytes=%d-%d" , offset , offset + count - 1 )) //
146155 .get () //
147156 .url (absoluteURLFrom (path ));
148157 return read (getRequest , progressListener );
@@ -156,7 +165,7 @@ private InputStream read(final Request.Builder getRequest, final ProgressListene
156165 final var countingBody = new ProgressResponseWrapper (response .body (), progressListener );
157166
158167 final int UNSATISFIABLE_RANGE = 416 ;
159- if (response .code () == UNSATISFIABLE_RANGE ) {
168+ if (response .code () == UNSATISFIABLE_RANGE ) {
160169 return new ByteArrayInputStream (new byte [0 ]);
161170 }
162171
@@ -178,8 +187,8 @@ CloudItemMetadata write(final CloudPath file, final boolean replace, final Input
178187 }
179188
180189 final var countingBody = new ProgressRequestWrapper (InputStreamRequestBody .from (data , size ), progressListener );
181- final var requestBuilder = new Request .Builder ()
182- .url (absoluteURLFrom (file ))
190+ final var requestBuilder = new Request .Builder () //
191+ .url (absoluteURLFrom (file )) //
183192 .put (countingBody );
184193
185194 try (final var response = httpClient .execute (requestBuilder )) {
@@ -199,7 +208,7 @@ private boolean exists(CloudPath path) throws CloudProviderException {
199208 }
200209
201210 CloudPath createFolder (final CloudPath path ) throws CloudProviderException {
202- if (exists (path )) {
211+ if (exists (path )) {
203212 throw new AlreadyExistsException (String .format ("Folder %s already exists" , path .toString ()));
204213 }
205214
@@ -228,8 +237,8 @@ void delete(final CloudPath path) throws CloudProviderException {
228237 }
229238
230239 void checkServerCompatibility () throws ServerNotWebdavCompatibleException {
231- final var optionsRequest = new Request .Builder ()
232- .method ("OPTIONS" , null )
240+ final var optionsRequest = new Request .Builder () //
241+ .method ("OPTIONS" , null ) //
233242 .url (baseUrl );
234243
235244 try (final var response = httpClient .execute (optionsRequest )) {
@@ -282,19 +291,20 @@ URL absoluteURLFrom(final CloudPath relativePath) {
282291 }
283292 }
284293
285- private enum PROPFIND_DEPTH {
286- ZERO ("0" ),
287- ONE ("1" ),
294+ private enum PropfindDepth {
295+ ZERO ("0" ), //
296+ ONE ("1" ), //
288297 INFINITY ("infinity" );
289298
290299 private final String value ;
291300
292- PROPFIND_DEPTH (final String value ) {
301+ PropfindDepth (final String value ) {
293302 this .value = value ;
294303 }
295304 }
296305
297306 static class WebDavAuthenticator {
307+
298308 static WebDavClient createAuthenticatedWebDavClient (final WebDavCredential webDavCredential ) throws ServerNotWebdavCompatibleException , UnauthorizedException {
299309 final var webDavClient = new WebDavClient (new WebDavCompatibleHttpClient (webDavCredential ), webDavCredential );
300310
0 commit comments