src-main.org.awakefw.file.servlet.ServerAwakeFileDispatch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of awake-file Show documentation
Show all versions of awake-file 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.
/*
* This file is part of Awake FILE.
* Awake FILE: Easy file upload & download over HTTP with Java.
* Copyright (C) 2014, 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.awakefw.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.awakefw.commons.api.server.AwakeCommonsConfigurator;
import org.awakefw.commons.server.util.AwakeServerLogger;
import org.awakefw.file.api.server.AwakeFileConfigurator;
import org.awakefw.file.api.server.fileaction.AwakeFileActionManager;
import org.awakefw.file.api.util.AwakeDebug;
import org.awakefw.file.api.util.HtmlConverter;
import org.awakefw.file.json.ListOfStringTransport;
import org.awakefw.file.servlet.util.CallUtil;
import org.awakefw.file.util.StringUtil;
import org.awakefw.file.util.Tag;
import org.awakefw.file.util.TransferStatus;
import org.awakefw.file.util.parms.Action;
import org.awakefw.file.util.parms.Parameter;
import org.awakefw.file.util.parms.ReturnCode;
import org.awakefw.file.version.AwakeFileVersionValues;
/**
* @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 ServerAwakeFileDispatch {
private static boolean DEBUG = AwakeDebug
.isSet(ServerAwakeFileDispatch.class);
// A space
public static final String SPACE = " ";
public static String CR_LF = System.getProperty("line.separator");
/**
* Constructor
*/
public ServerAwakeFileDispatch() {
}
/**
*
* Execute the dispatched request
*
* @param request
* the http request
* @param response
* the http response
* @param servletContextTempDir
* The temp dir used by Servlets
* @param awakeCommonsConfigurator
* the client commons configurator
* @param awakeFileConfigurator
* the client configurator for files
* @param awakeFileActionManager
* the client instance for File actions
* @throws IOException
* if any Servlet Exception occurs
*/
public void executeRequest(HttpServletRequest request,
HttpServletResponse response, File servletContextTempDir,
AwakeCommonsConfigurator awakeCommonsConfigurator,
AwakeFileConfigurator awakeFileConfigurator,
AwakeFileActionManager awakeFileActionManager) 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, awakeCommonsConfigurator,
awakeFileConfigurator, awakeFileActionManager);
return;
}
debug("ServerAwakeFileDispatch 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,
awakeCommonsConfigurator, 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);
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,
awakeCommonsConfigurator,
awakeFileConfigurator, 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 (!ServerAwakeFileDispatch.isTokenValid(username, token,
awakeCommonsConfigurator)) {
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/.awake/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_" + AwakeFileVersionValues.VERSION);
kawanNotifier.start();
}
// The filename
String filename = request.getParameter(Parameter.FILENAME);
filename = StringUtil.getTrimValue(filename);
// Treat all actions
if (action.equals(Action.DELETE_FILE_ACTION)) {
boolean result = awakeFileActionManager.delete(
awakeFileConfigurator, username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, "" + result);
} else if (action.equals(Action.EXISTS_ACTION)) {
boolean result = awakeFileActionManager.exists(
awakeFileConfigurator, username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, "" + result);
} else if (action.equals(Action.MKDIR_ACTION)) {
boolean result = awakeFileActionManager.mkdir(
awakeFileConfigurator, username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, "" + result);
} else if (action.equals(Action.MKDIR_ACTION)) {
boolean result = awakeFileActionManager.mkdir(
awakeFileConfigurator, username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, "" + result);
} else if (action.equals(Action.MKDIRS_ACTION)) {
boolean result = awakeFileActionManager.mkdirs(
awakeFileConfigurator, username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, "" + result);
} else if (action.equals(Action.LIST_DIRS_IN_DIR_ACTION)
|| action.equals(Action.LIST_FILES_IN_DIR_ACTION)) {
actionListDirsOrFiles(awakeFileConfigurator,
awakeFileActionManager, username, filename, action, out);
} else if (action.equals(Action.GET_FILE_LENGTH_ACTION)) {
long result = actionGetListFileLength(awakeFileConfigurator,
awakeFileActionManager, username, filename);
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, Long.toString(result));
} else if (action.equals(Action.DOWNLOAD_FILE_ACTION)) {
boolean result = awakeFileActionManager.download(out,
awakeFileConfigurator, username, filename);
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 if (action.equals(Action.CALL_ACTION)
|| action.equals(Action.CALL_ACTION_HTML_ENCODED)) {
ServerCallAction serverCallAction = new ServerCallAction();
serverCallAction.call(request, awakeCommonsConfigurator,
awakeFileConfigurator, out, username);
} else {
throw new IllegalArgumentException("Invalid Client Action: "
+ action);
}
return;
} catch (Exception e) {
writeLine(out, TransferStatus.SEND_FAILED);
writeLine(out, e.getClass().getName()); // Exception class name
writeLine(out, ServerUserException.getMessage(e)); // Exception
// message
writeLine(out, ExceptionUtils.getStackTrace(e)); // stack trace
try {
AwakeServerLogger.log(Level.WARNING, Tag.AWAKE_EXCEPTION_RAISED
+ ServerUserException.getMessage(e));
AwakeServerLogger.log(Level.WARNING, Tag.AWAKE_EXCEPTION_RAISED
+ ExceptionUtils.getStackTrace(e));
} catch (Exception e1) {
e1.printStackTrace();
e1.printStackTrace(System.out);
}
}
}
/**
* 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 awakeCommonsConfigurator
* the user configuration
*
* @return true if the pair (username, token) is verified and ok.
* @throws Exception
*/
public static boolean isTokenValid(String username, String token,
AwakeCommonsConfigurator awakeCommonsConfigurator) throws Exception {
// OK! Now build a token with SHA-1(username + secretValue)
String tokenRecomputed = AwakeCommonsConfiguratorCall.computeAuthToken(
awakeCommonsConfigurator, username);
if (token == null || !token.equals(tokenRecomputed)) {
debug("username : " + username + ":");
debug("token : " + token + ":");
debug("tokenRecomputed: " + tokenRecomputed + ":");
return false;
}
return true;
}
/**
* Action: List all sub-directories in a directory
*
* @param awakeFileConfigurator
* the user configuration
* @param awakeFileActionManager
* the user file manager
* @param username
* the client login
* @param filename
* the full path of directory to list files od directory
* @param out
* the output stream
* @throws IOException
*/
private void actionListDirsOrFiles(
AwakeFileConfigurator awakeFileConfigurator,
AwakeFileActionManager awakeFileActionManager, String username,
String filename, String action, OutputStream out)
throws IOException {
debug("actionListDirsOrFiles: " + action);
List files = null;
if (action.equals(Action.LIST_FILES_IN_DIR_ACTION)) {
files = awakeFileActionManager.listFiles(awakeFileConfigurator,
username, filename);
} else if (action.equals(Action.LIST_DIRS_IN_DIR_ACTION)) {
files = awakeFileActionManager.listDirectories(
awakeFileConfigurator, username, filename);
} else {
throw new IllegalArgumentException(Tag.AWAKE_PRODUCT_FAIL
+ "Invalid list dirs/files action: " + action);
}
if (files == null) {
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, null);
return;
}
if (files.size() == 0) {
writeLine(out, TransferStatus.SEND_OK);
writeLine(out, files.toString()); // []
return;
}
writeLine(out, TransferStatus.SEND_OK);
for (File file : files) {
String theFile = file.getName();
theFile = HtmlConverter.toHtml(theFile);
writeLine(out, theFile);
}
}
/**
* Action: delete a file list
*
* @param awakeFileConfigurator
* @param awakeFileActionManager
*
* @param filename
* the filelist
*/
private long actionGetListFileLength(
AwakeFileConfigurator awakeFileConfigurator,
AwakeFileActionManager awakeFileActionManager, 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 += awakeFileActionManager.length(awakeFileConfigurator,
username, theFilename);
}
return result;
}
private static void debug(String s) {
if (DEBUG) {
AwakeServerLogger.log(s);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy