org.kawanfw.file.servlet.ServerFileDispatch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of awake-file-server Show documentation
Show all versions of awake-file-server Show documentation
Awake FILE is a secure Open Source framework that allows to program very easily file uploads/downloads and RPC through http. File transfers include
powerful features like file chunking and automatic recovery mechanism.
Security has been taken into account from the design: server side allows
to specify strong security rules in order to protect the files and to secure the RPC calls.
The newest version!
/*
* This file is part of Awake FILE.
* Awake file: Easy file upload & download over HTTP with Java.
* Copyright (C) 2015, KawanSoft SAS
* (http://www.kawansoft.com). All rights reserved.
*
* Awake FILE is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Awake FILE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Any modifications to this file must keep this entire header
* intact.
*/
package org.kawanfw.file.servlet;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.logging.Level;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.kawanfw.commons.api.server.CommonsConfigurator;
import org.kawanfw.commons.json.ListOfStringTransport;
import org.kawanfw.commons.server.util.ServerLogger;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.commons.util.HtmlConverter;
import org.kawanfw.commons.util.StringUtil;
import org.kawanfw.commons.util.Tag;
import org.kawanfw.commons.util.TransferStatus;
import org.kawanfw.file.api.server.FileConfigurator;
import org.kawanfw.file.reflection.ClassPathUtil;
import org.kawanfw.file.servlet.nio.FileListAction;
import org.kawanfw.file.servlet.nio.FileListFilesAction;
import org.kawanfw.file.servlet.nio.FileMethodOneReturnAction;
import org.kawanfw.file.servlet.nio.KawanfwSecurityManager;
import org.kawanfw.file.servlet.util.CallUtil;
import org.kawanfw.file.servlet.util.FileTransferManager;
import org.kawanfw.file.servlet.util.HttpConfigurationUtil;
import org.kawanfw.file.util.parms.Action;
import org.kawanfw.file.util.parms.Parameter;
import org.kawanfw.file.util.parms.ReturnCode;
import org.kawanfw.file.version.FileVersionValues;
/**
* @author Nicolas de Pomereu
*
* The method executeRequest() is to to be called from the
* ServerCallerRecv Servlet and Class.
* It will execute a client side request with a ServerCaller.call()
* instance.
*
*/
public class ServerFileDispatch {
private static boolean DEBUG = FrameworkDebug
.isSet(ServerFileDispatch.class);
// A space
public static final String SPACE = " ";
public static String CR_LF = System.getProperty("line.separator");
public static KawanfwSecurityManager securityManager = null;
/**
* Constructor
*/
public ServerFileDispatch() {
}
/**
*
* Execute the dispatched request
*
* @param request
* the http request
* @param response
* the http response
* @param servletContextTempDir
* The temp dir used by Servlets
* @param commonsConfigurator
* the client commons configurator
* @param fileConfigurator
* the client configurator for files
* @throws IOException
* if any Servlet Exception occurs
*/
public void executeRequest(HttpServletRequest request,
HttpServletResponse response, File servletContextTempDir,
CommonsConfigurator commonsConfigurator,
FileConfigurator fileConfigurator) throws IOException {
OutputStream out = null;
try {
// Immediate catch if we are asking a file upload, because
// parameters are
// in unknown sequence. We know it's a upload action if it's mime
// multipart
if (ServletFileUpload.isMultipartContent(request)) {
ServerFileUploadAction serverFileUploadAction = new ServerFileUploadAction();
serverFileUploadAction.executeAction(request, response,
servletContextTempDir, commonsConfigurator,
fileConfigurator);
return;
}
debug("ServerFileDispatch begin 2");
// The action & filename (for file size ask)
String action = null;
// We must trap the IllegalArgumentException to rethrow properly to
// client
// This happens if there is an encryption problem
try {
action = request.getParameter(Parameter.ACTION);
} catch (IllegalArgumentException e) {
out = response.getOutputStream();
throw e;
}
action = StringUtil.getTrimValue(action);
debug("ACTION : " + action);
// Special action for Login, because Token does not exists and must
// be built
if (action.equals(Action.LOGIN_ACTION)
|| action.equals(Action.BEFORE_LOGIN_ACTION)) {
ServerLoginAction serverLoginAction = new ServerLoginAction();
serverLoginAction.executeAction(request, response,
commonsConfigurator, action);
return;
}
out = response.getOutputStream();
// Only if there is a call action, we may execute authorized classes
// without authentication/login
if (action.equals(Action.CALL_ACTION)
|| action.equals(Action.CALL_ACTION_HTML_ENCODED)) {
// The class name
String methodName = request.getParameter(Parameter.METHOD_NAME);
methodName = StringUtil.getTrimValue(methodName);
String className = StringUtils.substringBeforeLast(methodName,
".");
Class> c = Class.forName(className);
CallUtil callUtil = new CallUtil(c, fileConfigurator);
boolean callAllowed = callUtil.isCallableNotAuthenticated();
if (callAllowed) {
if (action.equals(Action.CALL_ACTION)
|| action.equals(Action.CALL_ACTION_HTML_ENCODED)) {
ServerCallAction serverCallAction = new ServerCallAction();
serverCallAction.call(request, commonsConfigurator,
fileConfigurator, out, null);
}
return;
}
}
// For all other actions, we check the parameters
// The username (used for token re-compilation)
String username = request.getParameter(Parameter.USERNAME);
username = StringUtil.getTrimValue(username);
debug("username : " + username);
// // For old call():
// try {
// username = StringUtil.fromBase64(username);
// } catch (Exception e) {
// } // The login may be in clear
// Authentication Token with SHA-1(login + secret value)
String token = request.getParameter(Parameter.TOKEN);
token = StringUtil.getTrimValue(token);
if (!ServerFileDispatch.isTokenValid(username, token,
commonsConfigurator)) {
debug("invalid token!");
debug("username: " + username);
debug("token : " + token);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, ReturnCode.INVALID_LOGIN_OR_PASSWORD);
return;
}
// Notify to Kawan in async mode using a secured Thread that
// the user has successfully logged (done once in JVM session per
// username).
// No notification is done if user.home/.kawansoft/no_notify.txt
// exists
// or web server name is localhost or 127.0.0.1
if (!KawanNotifier.existsNoNotifyTxt()
&& !KawanNotifier.usernameAlreadyLogged(username)
&& !KawanNotifier.serverNameIsLocalhost()) {
KawanNotifier kawanNotifier = new KawanNotifier(username,
"AwakeFile_" + FileVersionValues.VERSION);
kawanNotifier.start();
}
// Ok, install our security manager
//installSecurityManager(fileConfigurator);
// Displays class path
if (DEBUG) ClassPathUtil.displayClasspath();
// The filename
String filename = request.getParameter(Parameter.FILENAME);
filename = StringUtil.getTrimValue(filename);
// Call to a File method that returns one result (no list return)
if (action.equals(Action.FILE_METHOD_ONE_RETURN_ACTION)) {
FileMethodOneReturnAction fileMethodOneReturnAction = new FileMethodOneReturnAction();
fileMethodOneReturnAction.call(request, commonsConfigurator,
fileConfigurator, out, username, filename);
return;
}
// Call to a File.list() or File.list(FilenameFilter)
else if (action.equals(Action.FILE_LIST_ACTION)) {
FileListAction fileListAction = new FileListAction();
fileListAction.list(request, commonsConfigurator,
fileConfigurator, out, username, filename);
return;
}
// Call to a File.listFiles() or File.listFiles(FileFilter)
// or File.listFiles(FilenameFilter)
else if (action.equals(Action.FILE_LIST_FILES_ACTION)) {
FileListFilesAction fileListFilesAction = new FileListFilesAction();
fileListFilesAction.listFiles(request, commonsConfigurator,
fileConfigurator, out, username, filename);
return;
} else if (action.equals(Action.CALL_ACTION)
|| action.equals(Action.CALL_ACTION_HTML_ENCODED)) {
ServerCallAction serverCallAction = new ServerCallAction();
serverCallAction.call(request, commonsConfigurator,
fileConfigurator, out, username);
return;
} else if (action.equals(Action.GET_FILE_LENGTH_ACTION)) {
long result = actionGetListFileLength(fileConfigurator,
username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, Long.toString(result));
} else if (action.equals(Action.GET_JAVA_VERSION)) {
String javaVersion = System.getProperty("java.version");
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, javaVersion);
} else if (action.equals(Action.DOWNLOAD_FILE_ACTION)) {
String chunkLengtgStr = request
.getParameter(Parameter.CHUNKLENGTH);
long chunkLength = Long.parseLong(chunkLengtgStr);
boolean result = new FileTransferManager().download(out,
fileConfigurator, username, filename, chunkLength);
if (!result) {
// Impossible to find the file on server
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, Tag.FileNotFoundException);
// throw new FileNotFoundException(
// "File not found on remote server: " + filename);
}
} else {
throw new IllegalArgumentException("Invalid Client Action: "
+ action);
}
return;
} catch (Throwable throwable) {
if (DEBUG) throwable.printStackTrace(System.out);
Throwable finalThrowable = getFinalThrowable(throwable);
writeLine(out, TransferStatus.SEND_FAILED);
writeLine(out, finalThrowable.getClass().getName()); // Exception class name
writeLine(out, ServerUserThrowable.getMessage(finalThrowable)); // Exception
// message
writeLine(out, ExceptionUtils.getStackTrace(finalThrowable)); // stack trace
try {
ServerLogger.getLogger().log(
Level.WARNING,
Tag.PRODUCT_EXCEPTION_RAISED + " "
+ ServerUserThrowable.getMessage(finalThrowable));
ServerLogger.getLogger().log(
Level.WARNING,
Tag.PRODUCT_EXCEPTION_RAISED + " "
+ ExceptionUtils.getStackTrace(finalThrowable));
} catch (Exception e1) {
e1.printStackTrace();
e1.printStackTrace(System.out);
}
}
}
/**
* Analyse the throwable and build the final Exception/Throwable
* @param throwable the input throwable thrown
* @return the new rewritten Throwable
*/
public static Throwable getFinalThrowable(Throwable throwable) {
Throwable finalThrowable = null;
Throwable cause = throwable.getCause();
if (cause != null && cause instanceof ClassNotFoundException
|| cause != null && cause instanceof NoClassDefFoundError) {
finalThrowable = new ClassNotFoundException(throwable.getMessage());
} else if (cause != null
&& cause instanceof UnsupportedClassVersionError) {
finalThrowable = new UnsupportedClassVersionError(
throwable.getMessage());
} else {
if (cause != null) {
finalThrowable = cause;
} else {
finalThrowable = throwable;
}
}
return finalThrowable;
}
/**
* NOT USED ANYMORE
* Install the Security Manager that restricts FileFilter and FilenameFilter
* to write/delete files.
* @param fileConfigurator the file configurator in use
*/
@SuppressWarnings("unused")
private void installSecurityManager(FileConfigurator fileConfigurator) {
// Ok, install our security manager
if (System.getSecurityManager() == null) {
securityManager = new KawanfwSecurityManager();
System.setSecurityManager(securityManager);
}
}
/**
* Write a line of string on the servlet output stream. Will add the
* necessary CR_LF
*
* @param out
* the servlet output stream
* @param s
* the string to write
* @throws IOException
*/
private void writeLine(OutputStream out, String s) throws IOException {
out.write((s + CR_LF).getBytes());
}
/**
* Check the validity of the (username, token) pair
* Will return false if the operation is now allowed!
*
* @param username
* the username to check
* @param token
* the associated token with the username
* @param commonsConfigurator
* the user configuration
*
* @return true if the pair (username, token) is verified and ok.
* @throws Exception
*/
public static boolean isTokenValid(String username, String token,
CommonsConfigurator commonsConfigurator) throws Exception {
// OK! Now build a token with SHA-1(username + secretValue)
String tokenRecomputed = CommonsConfiguratorCall.computeAuthToken(
commonsConfigurator, username);
if (token == null || !token.equals(tokenRecomputed)) {
debug("username : " + username + ":");
debug("token : " + token + ":");
debug("tokenRecomputed: " + tokenRecomputed + ":");
return false;
}
return true;
}
/**
* Action: get a file list length
*
* @param fileConfigurator
* @param filename
* the filelist
* @return the length of file list
*/
private long actionGetListFileLength(FileConfigurator fileConfigurator,
String username, String filename) throws IOException {
debug("Action.GET_FILE_LENGTH_ACTION");
long result = 0;
// We have in fact a list of files
List files = ListOfStringTransport.fromJson(filename);
// actionGetListFileLength: We must convert each element of List
// files from Html
files = HtmlConverter.fromHtml(files);
for (String theFilename : files) {
// result += fileActionManager.length(fileConfigurator,
// username, theFilename);
theFilename = HttpConfigurationUtil.addRootPath(fileConfigurator,
username, theFilename);
File file = new File(theFilename);
result += file.length();
}
return result;
}
private static void debug(String s) {
if (DEBUG) {
ServerLogger.getLogger().log(Level.WARNING, s);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy