Util.java
/*
* Copyright (c) 2007-2017 MetaSolutions AB
*
* 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.entrystore.rest.util;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.FileCleanerCleanup;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.entrystore.Entry;
import org.entrystore.rest.EntryStoreApplication;
import org.json.JSONException;
import org.json.JSONObject;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.data.Tag;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* A Util class for the REST module
*
* @author Hannes Ebner
*/
public class Util {
static Logger log = LoggerFactory.getLogger(Util.class);
public static Set<String> dangerousFileExtensions = new HashSet<>();
static {
dangerousFileExtensions.add("apk");
dangerousFileExtensions.add("app");
dangerousFileExtensions.add("asp");
dangerousFileExtensions.add("aspx");
dangerousFileExtensions.add("bat");
dangerousFileExtensions.add("bin");
dangerousFileExtensions.add("cab");
dangerousFileExtensions.add("cmd");
dangerousFileExtensions.add("com");
dangerousFileExtensions.add("command");
dangerousFileExtensions.add("cpl");
dangerousFileExtensions.add("csh");
dangerousFileExtensions.add("ex");
dangerousFileExtensions.add("exe");
dangerousFileExtensions.add("gadget");
dangerousFileExtensions.add("inf");
dangerousFileExtensions.add("ins");
dangerousFileExtensions.add("inx");
dangerousFileExtensions.add("ipa");
dangerousFileExtensions.add("isu");
dangerousFileExtensions.add("js");
dangerousFileExtensions.add("jse");
dangerousFileExtensions.add("jsp");
dangerousFileExtensions.add("jsx");
dangerousFileExtensions.add("ksh");
dangerousFileExtensions.add("lnk");
dangerousFileExtensions.add("msc");
dangerousFileExtensions.add("msi");
dangerousFileExtensions.add("msp");
dangerousFileExtensions.add("mst");
dangerousFileExtensions.add("osx");
dangerousFileExtensions.add("out");
dangerousFileExtensions.add("paf");
dangerousFileExtensions.add("pif");
dangerousFileExtensions.add("php");
dangerousFileExtensions.add("pl");
dangerousFileExtensions.add("plx");
dangerousFileExtensions.add("prg");
dangerousFileExtensions.add("ps1");
dangerousFileExtensions.add("rb");
dangerousFileExtensions.add("reg");
dangerousFileExtensions.add("rgs");
dangerousFileExtensions.add("run");
dangerousFileExtensions.add("scr");
dangerousFileExtensions.add("sct");
dangerousFileExtensions.add("shb");
dangerousFileExtensions.add("shs");
dangerousFileExtensions.add("u3p");
dangerousFileExtensions.add("vb");
dangerousFileExtensions.add("vbe");
dangerousFileExtensions.add("vbs");
dangerousFileExtensions.add("vbscript");
dangerousFileExtensions.add("workflow");
dangerousFileExtensions.add("ws");
dangerousFileExtensions.add("wsf");
dangerousFileExtensions.add("wsh");
}
/**
* Takes the parameters from the URL and puts them into a map instead.
*
* @param request
* the request which contains the parameters.
* @return A map with the parameters.
*/
public static HashMap<String, String> parseRequest(String request) {
HashMap<String, String> argsAndVal = new HashMap<>();
int r = request.lastIndexOf("?");
String req = request.substring(r + 1);
String[] arguments = StringUtils.split(req, '&');
try {
for (String argument : arguments) {
String[] elements = StringUtils.split(argument, '=');
// URLDecoder is for application/x-www-form-urlencoded decoding, which is not exact with URL params decoding
// (includes '+' to 'space' replacement), hence need to replace '+' with '%2B'
argsAndVal.put(elements[0], elements.length == 1 ? "" : URLDecoder.decode(elements[1].replace("+", "%2B"), UTF_8));
}
} catch (IndexOutOfBoundsException e) {
// special case!
argsAndVal.put(req, "");
}
return argsAndVal;
}
/**
* We support If-Unmodified-Since (this is a dirty hack due some
* shortcomings of the current Restlet version). Restlets seem to evaluate
* the conditions even before the representation methods are called, so our
* only way on reacting on HTTP conditions is here in the constructor by
* setting the affected date to null if the request is ok.
*
* THIS IS ONLY A HACK AND SHOULD BE REPLACED WITH PROPER HANDLING DIRECTLY
* IN RESTLETS WHEN SUPPORTED.
*/
public static void handleIfUnmodifiedSince(Entry entry, Request request) {
if (entry != null && request != null) {
Date modDate = entry.getModifiedDate();
Date unmodSince = request.getConditions().getUnmodifiedSince();
if (modDate != null && unmodSince != null) {
// We can't do a simple equals because the objects differ
// slightly because the HTTP date supplied in the
// If-Unmodified-Since parameter does not contain information
// about milliseconds
long dT = modDate.getTime() - unmodSince.getTime();
if (dT < 0) {
dT *= -1;
}
if (dT < 1000) {
request.getConditions().setUnmodifiedSince(null);
}
} else {
request.getConditions().setUnmodifiedSince(null);
}
}
}
public static JSONObject createResponseObject(int statusCode, String message) {
JSONObject obj = new JSONObject();
try {
obj.put("status", statusCode);
obj.put("message", message);
} catch (JSONException e) {
log.error(e.getMessage());
}
return obj;
}
public static RestletFileUpload createRestletFileUpload(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null");
}
// Content with size above 100kB is cached on disk
DiskFileItemFactory dfif = new DiskFileItemFactory(1024*100, null);
RestletFileUpload upload = new RestletFileUpload(dfif);
ServletContext sc = EntryStoreApplication.getServletContext(context);
if (sc != null) {
log.debug("Setting FileCleaningTracker on DiskFileItemFactory");
dfif.setFileCleaningTracker(FileCleanerCleanup.getFileCleaningTracker(sc));
} else {
log.debug("Unable to get ServletContext instance, no FileCleaningTracker assigned to DiskFileItemFactory");
}
return upload;
}
public static Tag createTag(Date date) {
return new Tag(Long.toString(date.getTime()), false);
}
public static String sanitizeFilename(String filename) {
String fileExt = FilenameUtils.getExtension(filename);
if (fileExt != null && dangerousFileExtensions.contains(fileExt.toLowerCase())) {
return filename + "_dangerous";
}
return filename;
}
}