44
44
45
45
import java .io .File ;
46
46
import java .io .FileFilter ;
47
- import java .io .FileNotFoundException ;
48
47
import java .io .IOException ;
49
48
import java .io .RandomAccessFile ;
50
49
import java .nio .file .Files ;
51
- import java .nio .file .StandardCopyOption ;
50
+ import java .nio .file .Path ;
51
+ import java .nio .file .Paths ;
52
52
import java .util .ArrayList ;
53
53
import java .util .Collection ;
54
54
import java .util .Iterator ;
68
68
import org .restlet .engine .io .IoUtils ;
69
69
import org .restlet .representation .Representation ;
70
70
import org .restlet .representation .Variant ;
71
+ import org .restlet .resource .Directory ;
71
72
72
73
/**
73
74
* Connector to the file resources accessible. Here is the list of parameters
@@ -171,16 +172,27 @@ private boolean checkMetadataConsistency(String fileName, Representation represe
171
172
172
173
@ Override
173
174
public Entity getEntity (String decodedPath ) {
174
- // Take care of the file separator.
175
175
return new FileEntity (
176
- new File ( LocalReference . localizePath ( decodedPath ) ),
176
+ getFileWithLocalizedPath ( decodedPath ),
177
177
getMetadataService ());
178
178
}
179
179
180
+ /**
181
+ * Returns a new {@link File} instance for the given path name.<br>
182
+ * It ensures to translate the "/" to the local supported file separator (mainly useful for Windows OS).
183
+ *
184
+ * @param path
185
+ * The Path of the file
186
+ * @return a new {@link File} instance for the given path name.
187
+ */
188
+ private static File getFileWithLocalizedPath (final String path ) {
189
+ return new File (LocalReference .localizePath (path ));
190
+ }
191
+
180
192
/**
181
193
* Returns the name of the extension to use to store the temporary content
182
194
* while uploading content via the PUT method. Defaults to "tmp".
183
- *
195
+ *
184
196
* @return The name of the extension to use to store the temporary content.
185
197
*/
186
198
public String getTemporaryExtension () {
@@ -201,13 +213,18 @@ protected void handleLocal(Request request, Response response,
201
213
}
202
214
203
215
protected void handleFile (Request request , Response response , String decodedPath ) {
204
- if (GET .equals (request .getMethod ())
216
+ final Directory directory = (Directory ) request .getAttributes ().get ("org.restlet.directory" );
217
+ final File fileWithLocalizedPath = getFileWithLocalizedPath (decodedPath );
218
+
219
+ if (!isFileInDirectory (directory , fileWithLocalizedPath )) {
220
+ response .setStatus (CLIENT_ERROR_FORBIDDEN );
221
+ } else if (GET .equals (request .getMethod ())
205
222
|| HEAD .equals (request .getMethod ())) {
206
223
handleEntityGet (request , response , getEntity (decodedPath ));
207
224
} else if (PUT .equals (request .getMethod ())) {
208
- handleFilePut (request , response , decodedPath , new File ( decodedPath ) );
225
+ handleFilePut (request , response , decodedPath , fileWithLocalizedPath );
209
226
} else if (DELETE .equals (request .getMethod ())) {
210
- handleFileDelete (response , new File ( decodedPath ) );
227
+ handleFileDelete (response , fileWithLocalizedPath );
211
228
} else {
212
229
response .setStatus (CLIENT_ERROR_METHOD_NOT_ALLOWED );
213
230
response .getAllowedMethods ().add (GET );
@@ -217,17 +234,48 @@ protected void handleFile(Request request, Response response, String decodedPath
217
234
}
218
235
}
219
236
237
+ /**
238
+ * Indicates whether the given file is located inside the root directory.
239
+ *
240
+ * @param directory
241
+ * The root directory
242
+ * @param file
243
+ * The file.
244
+ * @return True if the path is located under the root directory, false otherwise.
245
+ */
246
+ private static boolean isFileInDirectory (final Directory directory , final File file ) {
247
+ boolean result = true ;
248
+
249
+ if (directory != null ) {
250
+ final String fileAbsolute = directory .getRootRef ().getPath (true );
251
+ final String filePath ;
252
+
253
+ if (fileAbsolute .indexOf (':' ) == 2 | fileAbsolute .indexOf ('|' ) == 2 ) {
254
+ filePath = fileAbsolute .substring (1 );
255
+ }else {
256
+ filePath = fileAbsolute ;
257
+ }
258
+
259
+ final Path rootDirectoryPath = Paths .get (filePath ).normalize ();
260
+ final Path actualFilePath = file .toPath ().normalize ();
261
+ result = !rootDirectoryPath .relativize (actualFilePath ).toString ().startsWith (".." );
262
+ }
263
+
264
+ return result ;
265
+ }
266
+
220
267
/**
221
268
* Handles a DELETE call for the FILE protocol.
222
- *
269
+ *
223
270
* @param response
224
271
* The response to update.
225
272
* @param file
226
273
* The file or directory to delete.
227
274
*/
228
275
protected void handleFileDelete (Response response , File file ) {
229
276
if (file .isDirectory ()) {
230
- if (file .listFiles ().length == 0 ) {
277
+ final File [] files = file .listFiles ();
278
+ if (files == null || files .length == 0 ) {
231
279
if (IoUtils .delete (file )) {
232
280
response .setStatus (SUCCESS_NO_CONTENT );
233
281
} else {
@@ -436,7 +484,9 @@ private Status updateFileWithPartialContent(Request request, File file, Range ra
436
484
return replaceFileByTemporaryFile (request , file , tmp );
437
485
} catch (IOException ioe ) {
438
486
getLogger ().log (WARNING , "Unable to create the temporary file" , ioe );
439
- cleanTemporaryFileIfUploadNotResumed (tmp );
487
+ if (tmp != null ) {
488
+ cleanTemporaryFileIfUploadNotResumed (tmp );
489
+ }
440
490
return new Status (SERVER_ERROR_INTERNAL , "Unable to create a temporary file" );
441
491
}
442
492
}
@@ -508,9 +558,6 @@ private Status createFileWithPartialContent(Request request, File file, Range ra
508
558
return SUCCESS_CREATED ;
509
559
}
510
560
return SUCCESS_NO_CONTENT ;
511
- } catch (FileNotFoundException fnfe ) {
512
- getLogger ().log (WARNING , "Unable to create the new file" , fnfe );
513
- return new Status (SERVER_ERROR_INTERNAL , fnfe );
514
561
} catch (IOException ioe ) {
515
562
getLogger ().log (WARNING , "Unable to create the new file" , ioe );
516
563
return new Status (SERVER_ERROR_INTERNAL , ioe );
@@ -536,7 +583,7 @@ private Status createFile(Request request, File file) {
536
583
getLogger ().warning (message );
537
584
return new Status (SERVER_ERROR_INTERNAL , message );
538
585
}
539
-
586
+
540
587
private void cleanTemporaryFileIfUploadNotResumed (File tmp ) {
541
588
if (tmp .exists () && !isResumeUpload ()) {
542
589
IoUtils .delete (tmp );
@@ -591,7 +638,7 @@ private void updateFileExtension(StringBuilder fileName, Metadata metadata) {
591
638
String extension = getMetadataService ().getExtension (metadata );
592
639
593
640
if (extension != null ) {
594
- fileName .append ("." + extension );
641
+ fileName .append ("." ). append ( extension );
595
642
} else {
596
643
if (metadata .getParent () != null ) {
597
644
updateFileExtension (fileName , metadata .getParent ());
0 commit comments