1414import org .cryptomator .cloudaccess .api .exceptions .CloudProviderException ;
1515import org .cryptomator .cloudaccess .api .exceptions .InsufficientStorageException ;
1616import org .cryptomator .cloudaccess .api .exceptions .NotFoundException ;
17+ import org .cryptomator .cloudaccess .api .exceptions .ParentFolderDoesNotExistException ;
18+ import org .cryptomator .cloudaccess .api .exceptions .TypeMismatchException ;
1719import org .slf4j .Logger ;
1820import org .slf4j .LoggerFactory ;
1921import org .xml .sax .SAXException ;
@@ -48,19 +50,17 @@ public class WebDavClient {
4850 CloudItemMetadata itemMetadata (final CloudPath path ) throws CloudProviderException {
4951 LOG .trace ("itemMetadata {}" , path );
5052 try (final var response = executePropfindRequest (path , PropfindDepth .ZERO )) {
51- checkExecutionSucceeded (response .code ());
53+ checkPropfindExecutionSucceeded (response .code ());
5254
53- final var nodes = getEntriesFromResponse (response );
54-
55- return processGet (nodes , path );
55+ return processGet (getEntriesFromResponse (response ), path );
5656 } catch (IOException | SAXException e ) {
5757 throw new CloudProviderException (e );
5858 }
5959 }
6060
6161 Quota quota (final CloudPath folder ) throws CloudProviderException {
6262 LOG .trace ("quota {}" , folder );
63- final var body = "<?xml version=\" 1.0\" ?>\n " //
63+ final var body = "<?xml version=\" 1.0\" encoding= \" utf-8 \" ?>\n " //
6464 + "<d:propfind xmlns:d=\" DAV:\" >\n " //
6565 + "<d:prop>\n " //
6666 + "<d:quota-available-bytes/>\n " //
@@ -75,7 +75,7 @@ Quota quota(final CloudPath folder) throws CloudProviderException {
7575 .header ("Content-Type" , "text/xml" );
7676
7777 try (final var response = httpClient .execute (builder )) {
78- checkExecutionSucceeded (response .code ());
78+ checkPropfindExecutionSucceeded (response .code ());
7979
8080 try (final var responseBody = response .body ()) {
8181 return new PropfindResponseParser ().parseQuta (responseBody .byteStream ());
@@ -88,7 +88,7 @@ Quota quota(final CloudPath folder) throws CloudProviderException {
8888 CloudItemList list (final CloudPath folder ) throws CloudProviderException {
8989 LOG .trace ("list {}" , folder );
9090 try (final var response = executePropfindRequest (folder , PropfindDepth .ONE )) {
91- checkExecutionSucceeded (response .code ());
91+ checkPropfindExecutionSucceeded (response .code ());
9292
9393 final var nodes = getEntriesFromResponse (response );
9494
@@ -98,8 +98,24 @@ CloudItemList list(final CloudPath folder) throws CloudProviderException {
9898 }
9999 }
100100
101+ private void checkPropfindExecutionSucceeded (int responseCode ) {
102+ switch (responseCode ) {
103+ case HttpURLConnection .HTTP_UNAUTHORIZED :
104+ throw new UnauthorizedException ();
105+ case HttpURLConnection .HTTP_FORBIDDEN :
106+ throw new ForbiddenException ();
107+ case HttpURLConnection .HTTP_NOT_FOUND :
108+ throw new NotFoundException ();
109+ }
110+
111+ if (responseCode < 199 || responseCode > 300 ) {
112+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + responseCode );
113+ }
114+ }
115+
101116 private Response executePropfindRequest (final CloudPath path , final PropfindDepth propfindDepth ) throws IOException {
102- final var body = "<d:propfind xmlns:d=\" DAV:\" >\n " //
117+ final var body = "<?xml version=\" 1.0\" encoding=\" utf-8\" ?>\n " //
118+ + "<d:propfind xmlns:d=\" DAV:\" >\n " //
103119 + "<d:prop>\n " //
104120 + "<d:resourcetype />\n " //
105121 + "<d:getcontentlength />\n " //
@@ -166,13 +182,26 @@ CloudPath move(final CloudPath from, final CloudPath to, boolean replace) throws
166182 }
167183
168184 try (final var response = httpClient .execute (builder )) {
169- if (response .code () == HttpURLConnection .HTTP_PRECON_FAILED ) {
170- throw new AlreadyExistsException (absoluteURLFrom (to ).toExternalForm ());
185+ if (response .isSuccessful ()) {
186+ return to ;
187+ } else {
188+ switch (response .code ()) {
189+ case HttpURLConnection .HTTP_UNAUTHORIZED :
190+ throw new UnauthorizedException ();
191+ case HttpURLConnection .HTTP_FORBIDDEN :
192+ throw new ForbiddenException ();
193+ case HttpURLConnection .HTTP_NOT_FOUND :
194+ throw new NotFoundException ();
195+ case HttpURLConnection .HTTP_CONFLICT :
196+ throw new ParentFolderDoesNotExistException ();
197+ case HttpURLConnection .HTTP_PRECON_FAILED :
198+ throw new AlreadyExistsException (absoluteURLFrom (to ).toExternalForm ());
199+ case HTTP_INSUFFICIENT_STORAGE :
200+ throw new InsufficientStorageException ();
201+ default :
202+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + response .code ());
203+ }
171204 }
172-
173- checkExecutionSucceeded (response .code ());
174-
175- return to ;
176205 } catch (IOException e ) {
177206 throw new CloudProviderException (e );
178207 }
@@ -201,15 +230,23 @@ private InputStream read(final Request.Builder getRequest, final ProgressListene
201230 try {
202231 response = httpClient .execute (getRequest );
203232 final var countingBody = new ProgressResponseWrapper (response .body (), progressListener );
204-
205- final int UNSATISFIABLE_RANGE = 416 ;
206- if (response .code () == UNSATISFIABLE_RANGE ) {
207- return new ByteArrayInputStream (new byte [0 ]);
233+ if (response .isSuccessful ()) {
234+ success = true ;
235+ return countingBody .byteStream ();
236+ } else {
237+ switch (response .code ()) {
238+ case HttpURLConnection .HTTP_UNAUTHORIZED :
239+ throw new UnauthorizedException ();
240+ case HttpURLConnection .HTTP_FORBIDDEN :
241+ throw new ForbiddenException ();
242+ case HttpURLConnection .HTTP_NOT_FOUND :
243+ throw new NotFoundException ();
244+ case 416 : // UNSATISFIABLE_RANGE
245+ return new ByteArrayInputStream (new byte [0 ]);
246+ default :
247+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + response .code ());
248+ }
208249 }
209-
210- checkExecutionSucceeded (response .code ());
211- success = true ;
212- return countingBody .byteStream ();
213250 } catch (IOException e ) {
214251 throw new CloudProviderException (e );
215252 } finally {
@@ -233,7 +270,22 @@ void write(final CloudPath file, final boolean replace, final InputStream data,
233270 lastModified .ifPresent (instant -> requestBuilder .addHeader ("X-OC-Mtime" , String .valueOf (instant .getEpochSecond ())));
234271
235272 try (final var response = httpClient .execute (requestBuilder )) {
236- checkExecutionSucceeded (response .code ());
273+ if (!response .isSuccessful ()) {
274+ switch (response .code ()) {
275+ case HttpURLConnection .HTTP_UNAUTHORIZED :
276+ throw new UnauthorizedException ();
277+ case HttpURLConnection .HTTP_FORBIDDEN :
278+ throw new ForbiddenException ();
279+ case HttpURLConnection .HTTP_BAD_METHOD :
280+ throw new TypeMismatchException ();
281+ case HttpURLConnection .HTTP_CONFLICT :
282+ throw new ParentFolderDoesNotExistException ();
283+ case HTTP_INSUFFICIENT_STORAGE :
284+ throw new InsufficientStorageException ();
285+ default :
286+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + response .code ());
287+ }
288+ }
237289 } catch (IOException e ) {
238290 throw new CloudProviderException (e );
239291 }
@@ -249,17 +301,29 @@ private boolean exists(CloudPath path) throws CloudProviderException {
249301
250302 CloudPath createFolder (final CloudPath path ) throws CloudProviderException {
251303 LOG .trace ("createFolder {}" , path );
252- if (exists (path )) {
253- throw new AlreadyExistsException (String .format ("Folder %s already exists" , path .toString ()));
254- }
255-
256304 final var builder = new Request .Builder () //
257305 .method ("MKCOL" , null ) //
258306 .url (absoluteURLFrom (path ));
259307
260308 try (final var response = httpClient .execute (builder )) {
261- checkExecutionSucceeded (response .code ());
262- return path ;
309+ if (response .isSuccessful ()) {
310+ return path ;
311+ } else {
312+ switch (response .code ()) {
313+ case HttpURLConnection .HTTP_UNAUTHORIZED :
314+ throw new UnauthorizedException ();
315+ case HttpURLConnection .HTTP_FORBIDDEN :
316+ throw new ForbiddenException ();
317+ case HttpURLConnection .HTTP_BAD_METHOD :
318+ throw new AlreadyExistsException (String .format ("Folder %s already exists" , path .toString ()));
319+ case HttpURLConnection .HTTP_CONFLICT :
320+ throw new ParentFolderDoesNotExistException ();
321+ case HTTP_INSUFFICIENT_STORAGE :
322+ throw new InsufficientStorageException ();
323+ default :
324+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + response .code ());
325+ }
326+ }
263327 } catch (IOException e ) {
264328 throw new CloudProviderException (e );
265329 }
@@ -272,7 +336,18 @@ void delete(final CloudPath path) throws CloudProviderException {
272336 .url (absoluteURLFrom (path ));
273337
274338 try (final var response = httpClient .execute (builder )) {
275- checkExecutionSucceeded (response .code ());
339+ if (!response .isSuccessful ()) {
340+ switch (response .code ()) {
341+ case HttpURLConnection .HTTP_UNAUTHORIZED :
342+ throw new UnauthorizedException ();
343+ case HttpURLConnection .HTTP_FORBIDDEN :
344+ throw new ForbiddenException ();
345+ case HttpURLConnection .HTTP_NOT_FOUND :
346+ throw new NotFoundException (String .format ("Node %s doesn't exists" , path .toString ()));
347+ default :
348+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + response .code ());
349+ }
350+ }
276351 } catch (IOException e ) {
277352 throw new CloudProviderException (e );
278353 }
@@ -285,10 +360,20 @@ void checkServerCompatibility() throws ServerNotWebdavCompatibleException {
285360 .url (baseUrl );
286361
287362 try (final var response = httpClient .execute (optionsRequest )) {
288- checkExecutionSucceeded (response .code ());
289- final var containsDavHeader = response .headers ().names ().contains ("DAV" );
290- if (!containsDavHeader ) {
291- throw new ServerNotWebdavCompatibleException ();
363+ if (response .isSuccessful ()) {
364+ final var containsDavHeader = response .headers ().names ().contains ("DAV" );
365+ if (!containsDavHeader ) {
366+ throw new ServerNotWebdavCompatibleException ();
367+ }
368+ } else {
369+ switch (response .code ()) {
370+ case HttpURLConnection .HTTP_UNAUTHORIZED :
371+ throw new UnauthorizedException ();
372+ case HttpURLConnection .HTTP_FORBIDDEN :
373+ throw new ForbiddenException ();
374+ default :
375+ throw new CloudProviderException ("Response code isn't between 200 and 300: " + response .code ());
376+ }
292377 }
293378 } catch (IOException e ) {
294379 throw new CloudProviderException (e );
@@ -297,37 +382,13 @@ void checkServerCompatibility() throws ServerNotWebdavCompatibleException {
297382
298383 void tryAuthenticatedRequest () throws UnauthorizedException {
299384 LOG .trace ("tryAuthenticatedRequest" );
300- try {
301- itemMetadata (CloudPath .of ("/" ));
302- } catch (Exception e ) {
303- if (e instanceof UnauthorizedException ) {
304- throw e ;
305- }
306- }
307- }
308-
309- private void checkExecutionSucceeded (final int status ) throws CloudProviderException {
310- switch (status ) {
311- case HttpURLConnection .HTTP_UNAUTHORIZED :
312- throw new UnauthorizedException ();
313- case HttpURLConnection .HTTP_FORBIDDEN :
314- throw new ForbiddenException ();
315- case HttpURLConnection .HTTP_NOT_FOUND : // fall through
316- case HttpURLConnection .HTTP_CONFLICT : //
317- throw new NotFoundException ();
318- case HTTP_INSUFFICIENT_STORAGE :
319- throw new InsufficientStorageException ();
320- }
321-
322- if (status < 199 || status > 300 ) {
323- throw new CloudProviderException ("Response code isn't between 200 and 300: " + status );
324- }
385+ itemMetadata (CloudPath .of ("/" ));
325386 }
326387
327388 // visible for testing
328389 URL absoluteURLFrom (final CloudPath relativePath ) {
329390 var basePath = CloudPath .of (baseUrl .getPath ()).toAbsolutePath ();
330- var fullPath = IntStream .range (0 , relativePath .getNameCount ()).mapToObj (i -> relativePath . getName ( i ) ).reduce (basePath , CloudPath ::resolve );
391+ var fullPath = IntStream .range (0 , relativePath .getNameCount ()).mapToObj (relativePath :: getName ).reduce (basePath , CloudPath ::resolve );
331392 try {
332393 return new URL (baseUrl , fullPath .toString ());
333394 } catch (MalformedURLException e ) {
0 commit comments