Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * JBoss, Home of Professional Open Source.
   * Copyright 2014 Red Hat, Inc., and individual contributors
   * as indicated by the @author tags.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
 
 package io.undertow.server.handlers.resource;
 
 import java.io.File;
 import java.util.List;
 
 import org.xnio.Xnio;

Serves files from the file system.
 
 public class FileResourceManager implements ResourceManager {
 
     private final List<ResourceChangeListenerlisteners = new ArrayList<>();
 
 
     private volatile String base;

    
Size to use direct FS to network transfer (if supported by OS/JDK) instead of read/write
 
     private final long transferMinSize;

    
Check to validate caseSensitive issues for specific case-insensitive FS.

 
     private final boolean caseSensitive;

    
Check to allow follow symbolic links
 
     private final boolean followLinks;

    
Used if followLinks == true. Set of paths valid to follow symbolic links
 
     private final TreeSet<StringsafePaths = new TreeSet<String>();
 
     public FileResourceManager(final File baselong transferMinSize) {
         this(basetransferMinSizetruefalsenull);
     }
 
     public FileResourceManager(final File baselong transferMinSizeboolean caseSensitive) {
         this(basetransferMinSizecaseSensitivefalsenull);
     }
 
     public FileResourceManager(final File baselong transferMinSizeboolean followLinksfinal String... safePaths) {
         this(basetransferMinSizetruefollowLinkssafePaths);
     }
 
     public FileResourceManager(final File baselong transferMinSizeboolean caseSensitiveboolean followLinksfinal String... safePaths) {
         if (base == null) {
             throw ..argumentCannotBeNull("base");
         }
         String basePath = base.getAbsolutePath();
         if (!basePath.endsWith("/")) {
             basePath = basePath + '/';
         }
         this. = basePath;
         this. = transferMinSize;
         this. = caseSensitive;
         this. = followLinks;
         if (this.) {
             if (safePaths == null) {
                 throw ..argumentCannotBeNull("safePaths");
             }
             for (final String safePath : safePaths) {
                if (safePath == null) {
                    throw ..argumentCannotBeNull("safePaths");
                }
            }
            this..addAll(Arrays.asList(safePaths));
        }
    }
    public File getBase() {
        return new File();
    }
    public FileResourceManager setBase(final File base) {
        if (base == null) {
            throw ..argumentCannotBeNull("base");
        }
        String basePath = base.getAbsolutePath();
        if (!basePath.endsWith("/")) {
            basePath = basePath + '/';
        }
        this. = basePath;
        return this;
    }
    public Resource getResource(final String p) {
        String path = null;
        //base always ends with a /
        if (p.startsWith("/")) {
            path = p.substring(1);
        } else {
            path = p;
        }
        try {
            File file = new File(path);
            if (file.exists()) {
                boolean isSymlinkPath = isSymlinkPath(file);
                if (isSymlinkPath) {
                    if (this. && isSymlinkSafe(file)) {
                        return getFileResource(filepath);
                    }
                } else {
                    return getFileResource(filepath);
                }
            }
            return null;
        } catch (Exception e) {
            ..debugf(e"Invalid path %s");
            return null;
        }
    }
    @Override
    public boolean isResourceChangeListenerSupported() {
        return true;
    }
    @Override
    public synchronized void registerResourceChangeListener(ResourceChangeListener listener) {
        .add(listener);
        if ( == null) {
             = Xnio.getInstance().createFileSystemWatcher("Watcher for " + .);
            .watchPath(new File(), new FileChangeCallback() {
                @Override
                public void handleChanges(Collection<FileChangeEventchanges) {
                    synchronized (FileResourceManager.this) {
                        final List<ResourceChangeEventevents = new ArrayList<>();
                        for (FileChangeEvent change : changes) {
                            if (change.getFile().getAbsolutePath().startsWith()) {
                                String path = change.getFile().getAbsolutePath().substring(.length());
                                events.add(new ResourceChangeEvent(path, ResourceChangeEvent.Type.valueOf(change.getType().name())));
                            }
                        }
                        for (ResourceChangeListener listener : ) {
                            listener.handleChanges(events);
                        }
                    }
                }
            });
        }
    }
    @Override
    public synchronized void removeResourceChangeListener(ResourceChangeListener listener) {
        .remove(listener);
    }
    public long getTransferMinSize() {
        return ;
    }
    @Override
    public synchronized void close() throws IOException {
        if ( != null) {
            .close();
        }
    }

    
Returns true is some element of path inside base path is a symlink.
    private boolean isSymlinkPath(final String basefinal File filethrows IOException {
        Path path = file.toPath();
        int nameCount = path.getNameCount();
        File root = new File(base);
        Path rootPath = root.toPath();
        int rootCount = rootPath.getNameCount();
        if (nameCount > rootCount) {
            File f = root;
            for (int irootCounti<nameCounti++) {
                f = new File(fpath.getName(i).toString());
                if (Files.isSymbolicLink(f.toPath())) {
                    return true;
                }
            }
        }
        return false;
    }

    
Security check for case insensitive file systems. We make sure the case of the filename matches the case of the request. This is only a check for case sensitivity, not for non canonical . and ../ which are allowed. For example: file.getName() == "page.jsp" && file.getCanonicalFile().getName() == "page.jsp" should return true file.getName() == "page.jsp" && file.getCanonicalFile().getName() == "page.JSP" should return false file.getName() == "./page.jsp" && file.getCanonicalFile().getName() == "page.jsp" should return true
    private boolean isFileSameCase(final File filethrows IOException {
        String canonicalName = file.getCanonicalFile().getName();
        if (canonicalName.equals(file.getName())) {
            return true;
        } else {
            return !canonicalName.equalsIgnoreCase(file.getName());
        }
    }

    
Security check for followSymlinks feature. Only follows those symbolink links defined in safePaths.
    private boolean isSymlinkSafe(final File filethrows IOException {
        String canonicalPath = file.getCanonicalPath();
        for (String safePath : this.) {
            if (safePath.length() > 0) {
                if (safePath.charAt(0) == '/') {
                    /*
                     * Absolute path
                     */
                    return safePath.length() > 0 &&
                            canonicalPath.length() >= safePath.length() &&
                            canonicalPath.startsWith(safePath);
                } else {
                    /*
                     * In relative path we build the path appending to base
                     */
                    String absSafePath =  + '/' + safePath;
                    File absSafePathFile = new File(absSafePath);
                    String canonicalSafePath = absSafePathFile.getCanonicalPath();
                    return canonicalSafePath.length() > 0 &&
                            canonicalPath.length() >= canonicalSafePath.length() &&
                            canonicalPath.startsWith(canonicalSafePath);
                }
            }
        }
        return false;
    }

    
Apply security check for case insensitive file systems.
    private FileResource getFileResource(final File filefinal String paththrows IOException {
        if (this.) {
            if (isFileSameCase(file)) {
                return new FileResource(filethispath);
            } else {
                return null;
            }
        } else {
            return new FileResource(filethispath);
        }
    }
New to GrepCode? Check out our FAQ X