package org.ngbw.web.actions; import java.io.ByteArrayInputStream; import java.io.InputStream; import org.apache.log4j.Logger; import org.apache.struts2.interceptor.validation.SkipValidation; import org.ngbw.sdk.database.Task; /** * Struts action class to handle file download based on current task, and * request parameter "inputPath". * *
* To Do: this doesn't need to inherit from ManageTasks it just needs to be able * to get the current task and the workbench session from the SESSION. * *
* And be careful, if a StreamResult attribute is found in the base class but * not here, it may be called w/o the needed context and cause problems. That's * what happened when Paul added getContentLength to DataManager and why I had * to add it here (or I could have made this not inherit from ManageTasks which * is derived from DataManager). * */ @SuppressWarnings("serial") public class DownloadAction extends ManageTasks { private static final Logger logger = Logger.getLogger(DownloadAction.class.getName()); private String contentType; private String inputPath; InputStream is; public String getInputPath () { return inputPath; } public void setInputPath ( String inputPath ) { this.inputPath = inputPath; } @Override public InputStream getInputStream () { return is; } @SkipValidation private InputStream getTheData ( String inputPath ) throws Exception { Task task = null; String[] taskId = (String[]) getParameters().get(ID); if (taskId != null && taskId.length > 0) { setCurrentTask(getSelectedTask(Long.parseLong(taskId[0]))); } if ((task = getCurrentTask()) == null) { throw new Exception("No task selected."); } return getWorkbenchSession().getFileFromWorkingDirectory(task, inputPath); } @Override public long getDataLength () { try { return is.available(); } catch ( Exception e ) { logger.error(constructMessage(e, "Error retrieving size of data, returning zero.")); return 0; } } @Override public String download () { logger.debug("BEGIN: download()::String"); try { // Filter out user input file path and file name. // It is dangerous to allow users to specify file paths and file names. if (null != inputPath && !inputPath.trim().isEmpty()) { if (inputPath.contains("*") || inputPath.contains("$") || inputPath.contains("{") || inputPath.contains("}") || inputPath.trim().startsWith(".") || inputPath.trim().startsWith("/") || inputPath.trim().startsWith("~")) { throw new Exception("Invalid filename: " + inputPath.trim()); } if (inputPath.trim().endsWith(".") || inputPath.trim().endsWith("/")) { throw new Exception("Invalid filename: " + inputPath.trim()); } } is = getTheData(inputPath); } catch ( Throwable t ) { // An error is expected here if the user tries to download the file // as it's being deleted or after it's been deleted. logger.debug(t.getMessage(), t); is = new ByteArrayInputStream(new byte[0]); } contentType = "application/x-unknown"; logger.debug("END: download()::String"); return SUCCESS; } public String getContentDisposition () { return String.format("filename=\"%s\"", inputPath); } @Override public String getContentType () { return contentType; } }