Coverage Report - net.admin4j.ui.servlets.FileExplorerServlet
 
Classes in this File Line Coverage Branch Coverage Complexity
FileExplorerServlet
44%
67/151
38%
35/90
4.263
FileExplorerServlet$FileExplorerRestrictions
46%
6/13
N/A
4.263
 
 1  
 /*
 2  
  * This software is licensed under the Apache License, Version 2.0
 3  
  * (the "License") agreement; you may not use this file except in compliance with
 4  
  * the License.  You may obtain a copy of the License at
 5  
  * 
 6  
  *      http://www.apache.org/licenses/LICENSE-2.0
 7  
  * 
 8  
  * Unless required by applicable law or agreed to in writing, software
 9  
  * distributed under the License is distributed on an "AS IS" BASIS,
 10  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11  
  * See the License for the specific language governing permissions and
 12  
  * limitations under the License.
 13  
  */
 14  
 package net.admin4j.ui.servlets;
 15  
 
 16  
 import java.io.File;
 17  
 import java.io.FileInputStream;
 18  
 import java.io.IOException;
 19  
 import java.io.Serializable;
 20  
 import java.util.ArrayList;
 21  
 import java.util.HashMap;
 22  
 import java.util.List;
 23  
 import java.util.Map;
 24  
 import java.util.Set;
 25  
 import java.util.TreeSet;
 26  
 
 27  
 import javax.servlet.ServletConfig;
 28  
 import javax.servlet.ServletException;
 29  
 import javax.servlet.http.HttpServletRequest;
 30  
 import javax.servlet.http.HttpServletResponse;
 31  
 
 32  
 import net.admin4j.config.Admin4JConfiguration;
 33  
 import net.admin4j.deps.commons.fileupload.FileItem;
 34  
 import net.admin4j.deps.commons.fileupload.FileUploadException;
 35  
 import net.admin4j.deps.commons.fileupload.disk.DiskFileItemFactory;
 36  
 import net.admin4j.deps.commons.fileupload.servlet.ServletFileUpload;
 37  
 import net.admin4j.deps.commons.io.IOUtils;
 38  
 import net.admin4j.deps.commons.lang3.StringUtils;
 39  
 import net.admin4j.deps.commons.lang3.exception.ExceptionUtils;
 40  
 import net.admin4j.util.Admin4jRuntimeException;
 41  
 import net.admin4j.util.ServletUtils;
 42  
 import net.admin4j.vo.FileWrapperVO;
 43  
 
 44  
 
 45  
 /**
 46  
  * Provides a Web-based file explorer for application servers.
 47  
  * 
 48  
  * <p>You will need to map this servlet to an url pattern (e.g. '/admin4j/file').</p>
 49  
  * <p>This servlet does <b>not</b> require other web resources.</p>
 50  
  * <p>Init parameters for this servlet are as follows:</p>
 51  
  * <li>base.dir.name -- Optional.  Default user.dir system property setting.  Starting current directory.</li>
 52  
  * <li>restrict.to.base.dir -- Optional (true/false).  Default true.  Restricts exploring to the base directory and all subdirectories.</li>
 53  
  * <li>restrict.from.exec -- Optional (true/false).  Default true.  Restricts user from executing scripts or applications.</li>
 54  
  * <li>restrict.from.write -- Optional (true/false).  Default true.  Restricts user from uploading files.</li>
 55  
  * @author D. Ashmore
 56  
  * @since 1.0
 57  
  */
 58  6
 public class FileExplorerServlet extends AdminDisplayServlet {
 59  
 
 60  
     private static final long serialVersionUID = -3651856296828821466L;
 61  
     
 62  6
     private FileExplorerRestrictions fileExplorerRestrictions = new FileExplorerRestrictions();
 63  6
     private String baseDirectoryName = null;
 64  
     
 65  
     public static final String PUBLIC_HANDLE="fileExplorer";
 66  
     
 67  
     @Override
 68  
     public void init(ServletConfig config) throws ServletException {
 69  6
         super.init(config);
 70  
         
 71  6
         String restrictStr = ServletUtils.getConfigurationSetting(
 72  
                 new String[]{PUBLIC_HANDLE + ".restrict.to.base.dir", 
 73  
                         "restrict.to.base.dir"}, config);
 74  6
         if ("false".equalsIgnoreCase(restrictStr)) {
 75  0
             this.fileExplorerRestrictions.setRestrictToBase(false);
 76  
         }
 77  6
         else if ( Admin4JConfiguration.getFileExplorerRestrictToBaseDir() != null ) {
 78  0
             this.fileExplorerRestrictions.setRestrictToBase(Admin4JConfiguration.getFileExplorerRestrictToBaseDir());
 79  
         }
 80  
         
 81  6
         String restrictFromExecutionStr = ServletUtils.getConfigurationSetting(
 82  
                 new String[]{PUBLIC_HANDLE + ".restrict.from.exec", 
 83  
                         "restrict.from.exec"}, config);
 84  6
         if ("false".equalsIgnoreCase(restrictFromExecutionStr)) {
 85  0
             this.fileExplorerRestrictions.setRestrictFromExecution(false);
 86  
         }
 87  6
         else if ( Admin4JConfiguration.getFileExplorerRestrictFromExec() != null ) {
 88  0
             this.fileExplorerRestrictions.setRestrictFromExecution(Admin4JConfiguration.getFileExplorerRestrictFromExec());
 89  
         }
 90  
         
 91  6
         String restrictFromWriteStr = ServletUtils.getConfigurationSetting(
 92  
                 new String[]{PUBLIC_HANDLE + ".restrict.from.write", 
 93  
                         "restrict.from.write"}, config);
 94  6
         if ("false".equalsIgnoreCase(restrictFromWriteStr)) {
 95  0
             this.fileExplorerRestrictions.setRestrictFromWrite(false);
 96  
         }
 97  6
         else if ( Admin4JConfiguration.getFileExplorerRestrictFromWrite() != null ) {
 98  0
             this.fileExplorerRestrictions.setRestrictFromWrite(Admin4JConfiguration.getFileExplorerRestrictFromWrite());
 99  
         }
 100  
         
 101  6
         baseDirectoryName = ServletUtils.getConfigurationSetting(
 102  
                 new String[]{PUBLIC_HANDLE + ".base.dir.name", 
 103  
                         "base.dir.name"}, config);
 104  0
         if (StringUtils.isEmpty(baseDirectoryName)  && !StringUtils.isEmpty(Admin4JConfiguration.getFileExplorerBaseDirName())) {
 105  0
             baseDirectoryName = Admin4JConfiguration.getFileExplorerBaseDirName();
 106  
         }
 107  
         else {
 108  6
             baseDirectoryName = System.getProperty("user.dir");
 109  
         }
 110  
         
 111  6
         File baseDir = new File(baseDirectoryName);
 112  6
         if ( !baseDir.exists()) {
 113  0
             throw new Admin4jRuntimeException("Base Directory (base.dir.name) doesn't exist")
 114  
                 .addContextValue("base.dir.name", baseDirectoryName);
 115  
         }
 116  6
     }
 117  
     
 118  
     @Override
 119  
     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 120  
        
 121  3
         String displayDirectoryName = request.getParameter("dir");
 122  0
         if (StringUtils.isEmpty(displayDirectoryName)) {
 123  3
                 displayDirectoryName = request.getParameter("dirInMkDir");
 124  
         }
 125  3
         String downloadFileName = request.getParameter("download");
 126  3
         List<FileItem> fileItems = getMultipartRequestItems(request);
 127  3
         String baseDirectoryNameDuringUpload = getMulitpartRequestParam(fileItems, "dirInUpload");
 128  
         
 129  0
         if (StringUtils.isEmpty(displayDirectoryName) && StringUtils.isEmpty(baseDirectoryNameDuringUpload)) {
 130  3
             displayDirectoryName = this.baseDirectoryName;
 131  
         }
 132  0
         else if (StringUtils.isEmpty(displayDirectoryName)) {
 133  0
             displayDirectoryName = baseDirectoryNameDuringUpload;
 134  
         }
 135  
         
 136  0
         if (StringUtils.isEmpty(downloadFileName)) {
 137  3
             File baseDir = new File(displayDirectoryName);
 138  3
             String requestMessage = processRequest(request, baseDir, fileItems);
 139  3
             this.presentDirectoryListing(baseDir, requestMessage, request, response); 
 140  3
         }
 141  0
         else this.presentFileContent(downloadFileName, request, response);
 142  
         
 143  3
     }
 144  
     
 145  
     private void presentFileContent(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
 146  
 
 147  0
         File displayFile = new File(filename);
 148  0
         FileInputStream stream = new FileInputStream(displayFile);
 149  
         byte[] content = IOUtils.toByteArray(stream);
 150  0
         stream.close();
 151  
         
 152  0
         response.setContentType("application/octet-stream"); // or "application/octet-stream", your choice
 153  
         
 154  0
         response.setContentLength(content.length);
 155  0
         response.setHeader("Content-disposition", "attachment; filename=\"" + displayFile.getName() + "\"");
 156  
         try {
 157  0
             response.getOutputStream().write(content);
 158  0
         } catch (IOException e) {
 159  0
             throw new Admin4jRuntimeException("Failed to write out the file.", e);
 160  0
         }
 161  0
     }
 162  
     
 163  
     private void presentDirectoryListing(File baseDir, String requestMessage, HttpServletRequest request, HttpServletResponse response) throws IOException {
 164  3
         response.setContentType("text/html");
 165  
                 
 166  3
         Set<FileWrapperVO>  subDirectorySet = new TreeSet<FileWrapperVO>();
 167  3
         Set<FileWrapperVO>  fileSet = new TreeSet<FileWrapperVO>();
 168  3
         Set<FileWrapperVO>  rootSet = new TreeSet<FileWrapperVO>();
 169  
         
 170  81
         for (File file: baseDir.listFiles()) {
 171  78
             if (file.isDirectory())   subDirectorySet.add(new FileWrapperVO(file));
 172  36
             else fileSet.add(new FileWrapperVO(file));
 173  
         }
 174  15
         for (File file: File.listRoots()) {
 175  12
             rootSet.add(new FileWrapperVO(file));
 176  
         }
 177  
         
 178  3
         Map<String,Object> variableMap = new HashMap<String,Object>();
 179  3
         variableMap.put("restrictions", fileExplorerRestrictions);
 180  3
         variableMap.put("message", requestMessage);
 181  3
         variableMap.put("currentDirectory", new FileWrapperVO(baseDir));
 182  3
         if (baseDir.getParentFile() != null && !fileExplorerRestrictions.isRestrictToBase())
 183  
         {
 184  0
                 variableMap.put("parentDirectory", new FileWrapperVO(baseDir.getParentFile()));
 185  
         }
 186  3
         else if (baseDir.getParentFile() != null && fileExplorerRestrictions.isRestrictToBase() && isWithinBase(baseDir.getParentFile()))
 187  
         {
 188  0
             variableMap.put("parentDirectory", new FileWrapperVO(baseDir.getParentFile()));
 189  
         }
 190  3
         variableMap.put("subdirectoryList", subDirectorySet);
 191  3
         variableMap.put("rootList", rootSet);
 192  3
         variableMap.put("fileList", fileSet);
 193  
         
 194  3
         displayFreeMarkerPage(request, response, "fileExplorerServletDisplay.ftl", variableMap);
 195  
 
 196  3
     }
 197  
     
 198  
     private boolean isWithinBase(File dir) {
 199  3
         boolean answer = false;
 200  3
         File localDir = dir;
 201  3
         File baseDir = new File(this.baseDirectoryName);
 202  3
         if (baseDir.equals(dir)) {
 203  0
             answer = true;
 204  
         }
 205  
         
 206  15
         while ( !answer && localDir.getParentFile() != null) {
 207  12
             if (baseDir.equals(localDir.getParentFile())) {
 208  0
                 answer = true;
 209  
             }
 210  12
             localDir = localDir.getParentFile();
 211  
         }
 212  
         
 213  3
         return answer;
 214  
     }
 215  
 
 216  
     private String processRequest(HttpServletRequest request, File baseDir, List<FileItem> items) {
 217  3
         String runExecutableName = request.getParameter("run");
 218  3
         String deleteFileName = request.getParameter("delete");
 219  3
         String mkDirName = request.getParameter("dirName");
 220  
         
 221  3
         String message = "";
 222  0
         if ( !StringUtils.isEmpty(runExecutableName)) {
 223  0
             message = this.runExecutable(runExecutableName);
 224  
         }
 225  0
         else if (ServletFileUpload.isMultipartContent(request)) {
 226  0
             message = this.processUpload(request, baseDir, items);
 227  
         }
 228  0
         else if ( !StringUtils.isEmpty(deleteFileName)) {
 229  0
             message = this.processDelete(deleteFileName);
 230  
         }
 231  0
         else if ( !StringUtils.isEmpty(mkDirName)) {
 232  0
             message = this.processMkdir(mkDirName, baseDir);
 233  
         }
 234  3
         return message;
 235  
     }
 236  
     
 237  
     private String processUpload(HttpServletRequest request, File directory, List<FileItem> items) {
 238  0
         if (this.fileExplorerRestrictions.isRestrictFromWrite()) {
 239  0
             return "File Upload not allowed.  Modify restrict.from.write setting to allow file upload.";
 240  
         }
 241  
         
 242  0
         String message = "";
 243  
         try {
 244  
 //            FileItemFactory factory = new DiskFileItemFactory();
 245  
 //            ServletFileUpload upload = new ServletFileUpload(factory);
 246  
 //            List<FileItem> items = upload.parseRequest(request);
 247  
             File uploadedFile;
 248  0
             for (FileItem item : items) {
 249  0
                 if (!StringUtils.isEmpty(item.getName())) {
 250  
                     uploadedFile = new File(directory.getCanonicalPath() + "/"
 251  
                             + item.getName());
 252  
                     item.write(uploadedFile);
 253  
                     message="File " + item.getName() + " (" + item.getSize() + " bytes) Uploaded!";
 254  
                 }
 255  
             }
 256  0
         } catch (Exception e) {
 257  
             message = ExceptionUtils.getStackTrace(e);
 258  0
         }
 259  0
         return message;
 260  
     }
 261  
     
 262  
     private String processDelete(String deleteFileName) {
 263  0
         if (this.fileExplorerRestrictions.isRestrictFromWrite()) {
 264  0
             return "File delete not allowed.  Modify restrict.from.write setting to allow delete.";
 265  
         }
 266  
         
 267  0
         File deleteFile = new File(deleteFileName);
 268  0
         if (!deleteFile.delete()) {
 269  0
             return "File not deleted.  Reason not known.  See javadoc for File.delete().";
 270  
         }
 271  
         
 272  0
         return "File deleted";
 273  
     }
 274  
     
 275  
     private String processMkdir(String dirName, File baseDirectory) {
 276  0
         if (this.fileExplorerRestrictions.isRestrictFromWrite()) {
 277  0
             return "Directory creation not allowed.  Modify restrict.from.write setting to allow directory creation.";
 278  
         }
 279  
         
 280  0
         String message = null;
 281  
         try {
 282  0
             File newDir = new File(baseDirectory.getCanonicalPath() + "/"
 283  
                     + dirName);
 284  0
             if (newDir.mkdir()) {
 285  0
                 message = "New Directory created: " + dirName;
 286  
             }
 287  0
             else message = "New Directory not created: " + dirName + ".  Reason unknown.  See javadoc for File.mkdir().";
 288  0
         } catch (Exception e) {
 289  
             message = ExceptionUtils.getStackTrace(e);
 290  0
         }
 291  
         
 292  0
         return message;
 293  
     }
 294  
     
 295  
     private String runExecutable(String executableName) {
 296  
 
 297  0
         if (this.fileExplorerRestrictions.isRestrictFromExecution()) {
 298  0
             return "File execution not allowed.  Modify restrict.from.exec setting to allow execution.";
 299  
         }
 300  
         
 301  0
         File executableFile = new File(executableName);
 302  0
         StringBuffer message = new StringBuffer();
 303  
         
 304  
         try {
 305  0
             Process process = Runtime.getRuntime().exec(new String[]{executableName}, null, executableFile.getParentFile());
 306  
             
 307  
             String error = IOUtils.toString(process.getErrorStream());
 308  0
             if ( !StringUtils.isEmpty(error)) {
 309  0
                 message.append(error);
 310  
             }
 311  
             
 312  
             String stdOut = IOUtils.toString(process.getInputStream());
 313  0
             if ( !StringUtils.isEmpty(stdOut)) {
 314  0
                 message.append(stdOut);
 315  
             }
 316  0
         } catch (Throwable e) {
 317  0
             throw new Admin4jRuntimeException("error executing file.", e);
 318  0
         }
 319  
         
 320  0
         return message.toString();
 321  
     }
 322  
     
 323  6
     public static class FileExplorerRestrictions implements Serializable {
 324  
 
 325  
         private static final long serialVersionUID = -6396405187849729874L;
 326  6
         private boolean restrictToBase = true;
 327  6
         private boolean restrictFromExecution = true;
 328  6
         private boolean restrictFromWrite = true;
 329  
         
 330  
         public boolean isRestrictToBase() {
 331  6
             return restrictToBase;
 332  
         }
 333  
         
 334  
         public void setRestrictToBase(boolean restrictToBase) {
 335  0
             this.restrictToBase = restrictToBase;
 336  0
         }
 337  
         
 338  
         public boolean isRestrictFromExecution() {
 339  0
             return restrictFromExecution;
 340  
         }
 341  
         
 342  
         public void setRestrictFromExecution(boolean restrictFromExecution) {
 343  0
             this.restrictFromExecution = restrictFromExecution;
 344  0
         }
 345  
 
 346  
         public boolean isRestrictFromWrite() {
 347  81
             return restrictFromWrite;
 348  
         }
 349  
 
 350  
         public void setRestrictFromWrite(boolean restrictFromWrite) {
 351  0
             this.restrictFromWrite = restrictFromWrite;
 352  0
         }
 353  
     }
 354  
     
 355  
 
 356  
     @SuppressWarnings("unchecked")
 357  
         private List<FileItem> getMultipartRequestItems(HttpServletRequest request)
 358  
     {
 359  0
             if (!ServletFileUpload.isMultipartContent(request))
 360  3
                     return null;
 361  
             
 362  0
             List<FileItem> items = new ArrayList<FileItem>();
 363  
                 try {
 364  
                         items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
 365  0
                 } catch (FileUploadException e) {
 366  0
                         throw new RuntimeException("Could not parse multipart request.",e);
 367  0
                 }
 368  0
                 return items;
 369  
     }
 370  
     
 371  
     private String getMulitpartRequestParam(List<FileItem> items, String paramName)
 372  
     {
 373  3
             if (items == null)
 374  3
                     return null;
 375  0
             for (FileItem item : items) {
 376  0
                 if (item.isFormField() && paramName.equals(item.getFieldName())) 
 377  
                 {
 378  
                         return item.getString();
 379  
                 }
 380  
             }
 381  0
             return null;
 382  
             
 383  
     }
 384  
     
 385  
     /* (non-Javadoc)
 386  
      * @see net.admin4j.ui.servlets.Admin4JServlet#getServletLabel()
 387  
      */
 388  
     @Override
 389  
     public String getServletLabel() {
 390  3
         return "File Explorer";
 391  
     }
 392  
 }