/*
* Copyright (c) WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.humantask.ui.fileupload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.CarbonException;
import org.wso2.carbon.humantask.HumanInteractionsDocument;
import org.wso2.carbon.humantask.core.deployment.config.HTDeploymentConfigDocument;
import org.wso2.carbon.humantask.core.utils.FileUtils;
import org.wso2.carbon.humantask.ui.constants.HumanTaskUIConstants;
import org.wso2.carbon.ui.CarbonUIMessage;
import org.wso2.carbon.ui.transports.fileupload.AbstractFileUploadExecutor;
import org.wso2.carbon.utils.FileItemData;
import org.wso2.carbon.utils.ServerConstants;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* HumanTask archive upload executor
*/
public class HumanTaskUploadExecutor extends AbstractFileUploadExecutor {
private static Log log = LogFactory.getLog(HumanTaskUploadExecutor.class);
private static final String[] ALLOWED_FILE_EXTENSIONS =
new String[]{".zip"};
public boolean execute(HttpServletRequest request,
HttpServletResponse response) throws CarbonException, IOException {
String errMsg;
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
String webContext = (String) request.getAttribute(CarbonConstants.WEB_CONTEXT);
String serverURL = (String) request.getAttribute(CarbonConstants.SERVER_URL);
String cookie = (String) request.getAttribute(ServerConstants.ADMIN_SERVICE_COOKIE);
Map<String, ArrayList<FileItemData>> fileItemsMap = getFileItemsMap();
if (fileItemsMap == null || fileItemsMap.isEmpty()) {
String msg = "File uploading failed.";
log.error(msg);
out.write("<textarea>" +
"(function(){i18n.fileUplodedFailed();})();" +
"</textarea>");
return true;
}
HIUploaderClient uploaderClient = new HIUploaderClient(configurationContext,
serverURL +
HumanTaskUIConstants.SERVICE_NAMES.HUMANTASK_UPLOADER_SERVICE_NAME,
cookie);
try {
for (FileItemData fieldData : fileItemsMap.get("humantaskFileName")) {
String fileName = getFileName(fieldData.getFileItem().getName());
//Check filename for \ charactors. This cannot be handled at the lower stages.
if (fileName.matches("(.*[\\\\].*[/].*|.*[/].*[\\\\].*)")) {
log.error("HumanTask Package Validation Failure: one or many of the following illegal characters are in " +
"the package.\n ~!@#$;%^*()+={}[]| \\<>");
throw new Exception("HumanTask Package Validation Failure: one or many of the following illegal characters " +
"are in the package. ~!@#$;%^*()+={}[]| \\<>");
}
//Check file extension.
checkServiceFileExtensionValidity(fileName, ALLOWED_FILE_EXTENSIONS);
if (fileName.lastIndexOf('\\') != -1) {
int indexOfColon = fileName.lastIndexOf('\\') + 1;
fileName = fileName.substring(indexOfColon, fileName.length());
}
if ("humantaskFileName".equals(fieldData.getFileItem().getFieldName())) {
SaveExtractReturn uploadedFiles = saveAndExtractUploadedFile(fieldData.getFileItem());
validateHumanTaskPackage(uploadedFiles.extractedFile);
DataSource dataSource = new FileDataSource(uploadedFiles.zipFile);
uploaderClient.addUploadedFileItem(new DataHandler(dataSource), fileName, "zip");
}
}
uploaderClient.uploadFileItems();
String msg = "Your HumanTask package been uploaded successfully. Please refresh this page in a" +
" while to see the status of the new package.";
CarbonUIMessage.sendCarbonUIMessage(msg, CarbonUIMessage.INFO, request,
response, getContextRoot(request) + "/" + webContext +
HumanTaskUIConstants.PAGES.PACKAGE_LIST_PAGE);
return true;
} catch (Exception e) {
errMsg = "File upload failed :" + e.getMessage();
log.error(errMsg, e);
// Removing <, > and </ characters from Error message, in order to provide accurate error message.
// TODO : FIX this correctly. Identify why latest browsers unable to render HTML encoded string. Eg: < with <
String encodedErrMsg = errMsg.replace("</", " ").replace(">", " ").replace("<", " ");
CarbonUIMessage.sendCarbonUIMessage(encodedErrMsg, CarbonUIMessage.ERROR, request, response, getContextRoot(request) + "/" + webContext +
HumanTaskUIConstants.PAGES.UPLOAD_PAGE);
}
return false;
}
public SaveExtractReturn saveAndExtractUploadedFile(FileItem fileItem) throws Exception {
String serviceUploadDir = getTempUploadDir();
File servicesDir = new File(serviceUploadDir);
if (!servicesDir.exists() && !servicesDir.mkdirs()) {
throw new IOException("Fail to create the directory: " + servicesDir.getAbsolutePath());
}
// Writing HumanTask archive to file system
String fileItemName = getFileName(fileItem.getName());
File uploadedFile = new File(servicesDir, fileItemName);
if (log.isDebugEnabled()) {
log.debug("[HumanTaskUI]HumanTask Archive Path: " + uploadedFile.getAbsolutePath());
}
try {
fileItem.write(uploadedFile);
} catch (Exception e) {
log.error("Error occurred while writing file item to file system.", e);
throw new Exception("Error occurred while writing file item to file system.", e);
}
String destinationDir = serviceUploadDir + fileItemName.substring(0, fileItemName.lastIndexOf('.'));
if (log.isDebugEnabled()) {
log.debug("[HumanTaskUI]HumanTask package location: " + destinationDir);
}
try {
ArchiveExtractor.extract(uploadedFile, destinationDir);
} catch (Exception e) {
log.error("Error extracting archive.", e);
throw new Exception(e);
}
return new SaveExtractReturn(uploadedFile.getAbsolutePath(), destinationDir);
}
// private static void addDir(File dirObj, ZipOutputStream out, int basePathLen) throws Exception {
// File[] files = dirObj.listFiles();
// byte[] tmpBuf = new byte[2048];
//
// for (File file : files) {
// if (file.isDirectory()) {
// addDir(file, out, basePathLen);
// continue;
// }
// FileInputStream in = null;
// try {
// in = new FileInputStream(file.getAbsolutePath());
// if (log.isDebugEnabled()) {
// log.debug("Adding: " + file.getAbsolutePath());
// }
// out.putNextEntry(new ZipEntry(file.getAbsolutePath().substring(basePathLen)));
// int len;
// while ((len = in.read(tmpBuf)) > 0) {
// out.write(tmpBuf, 0, len);
// }
// } finally {
// if (in != null) {
// in.close();
// }
// }
// }
// }
private String getTempUploadDir() {
String uuid = generateUUID();
String tmpDir = "humantaskTemp";
return getWorkingDir() + File.separator + tmpDir + File.separator + uuid + File.separator;
}
public void validateHumanTaskPackage(String directoryPath) throws Exception {
// Check for valid HumanTask Deployment config.
validateHTDeploymentConfigDocument(directoryPath);
// Check for valid HumanTask Definition.
validateHumanTaskDefinition(directoryPath);
}
/**
* Validate HT deployment config Document.
*
* @param directoryPath Unzipped HumanTask archive directory.
* @throws Exception
*/
public void validateHTDeploymentConfigDocument(String directoryPath) throws Exception {
if (log.isDebugEnabled()) {
log.debug("Validating HumanTask deployment config.");
}
//We have to check whether the htconfig.xml file is in the root level.
//otherwise we cannot accept this as a valid human task package.
String htConfigFilePathString = directoryPath + File.separator + HumanTaskUIConstants.FILE_NAMES.HT_CONFIG_NAME;
File htConfigFile = new File(htConfigFilePathString);
if (!htConfigFile.exists()) {
throw new Exception("The uploaded task definition zip file does not contain a htconfig.xml" +
"file. Please check the package and re-upload.");
}
HTDeploymentConfigDocument hiConf;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(htConfigFile);
hiConf = HTDeploymentConfigDocument.Factory.parse(fileInputStream);
if (log.isDebugEnabled()) {
log.debug("Successfully Validated HumanTask deployment config.");
}
} catch (Exception e) {
String errMsg = "Error occurred while parsing the human interaction configuration " + "file: htconfig.xml";
throw new Exception(errMsg, e);
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
}
}
/**
* Validate HumanTask definition.
*
* @param directoryPath Unzipped HumanTask archive directory.
* @throws Exception
*/
public void validateHumanTaskDefinition(String directoryPath) throws Exception {
if (log.isDebugEnabled()) {
log.debug("Validating HumanTask definition.");
}
File humantaskDir = new File(directoryPath);
List<File> hiDefinitionFiles = FileUtils.directoryEntriesInPath(humantaskDir, humantaskFilter);
if (hiDefinitionFiles.size() != 1) {
String errMsg;
if (hiDefinitionFiles.size() == 0) {
errMsg = "No Humantask definition files was found. Please check the package and re-upload.";
} else {
errMsg = "Multiple (" + hiDefinitionFiles.size() +
") Humantask definition files were found. Only single task definition file (.ht) allowed.";
}
throw new Exception(errMsg);
}
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(hiDefinitionFiles.get(0));
// Check Task definition compliant with schema.
HumanInteractionsDocument humanInteractionsDocument =
HumanInteractionsDocument.Factory.parse(fileInputStream);
if (log.isDebugEnabled()) {
log.debug("successfully validate HumanTask definition.");
}
} catch (Exception e) {
String errMsg = "Error while reading Human Interactions definition. Reason : " + e.getMessage();
throw new Exception(errMsg, e);
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
}
}
static class SaveExtractReturn {
private String zipFile;
private String extractedFile;
public SaveExtractReturn(String zipFile, String extractedFile) {
this.zipFile = zipFile;
this.extractedFile = extractedFile;
}
}
private static final FileFilter humantaskFilter = new FileFilter() {
public boolean accept(File path) {
return path.getName().endsWith(HumanTaskUIConstants.FILE_NAMES.HT_FILE_EXT) && path.isFile();
}
};
}