package de.tud.kom.socom.components.influence; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.sql.SQLException; import java.util.Collection; import java.util.Map; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.json.JSONException; import org.json.JSONObject; import de.tud.kom.socom.GlobalConfig; import de.tud.kom.socom.database.influence.HSQLInfluenceDatabase; import de.tud.kom.socom.database.influence.InfluenceDatabase; import de.tud.kom.socom.util.SocomRequest; import de.tud.kom.socom.util.JSONUtils; import de.tud.kom.socom.util.ResourceLoader; import de.tud.kom.socom.util.exceptions.SocomException; import de.tud.kom.socom.util.exceptions.IllegalAccessException; import de.tud.kom.socom.util.exceptions.IllegalFileSizeException; import de.tud.kom.socom.util.exceptions.MediaTypeNotSupportedException; import de.tud.kom.socom.util.media.converter.FFMPEGConverter; import de.tud.kom.socom.util.media.converter.MediaConverter; import eu.medsea.mimeutil.MimeUtil; public class InfluenceUploadHandler { private static InfluenceDatabase db = HSQLInfluenceDatabase.getInstance(); private static final long MAX_SIZE_PICTURE_UPLOAD = 1024 /* kb */* 1024 /* MB */* 6; // 10MB private static final long MAX_SIZE_AUDIO_UPLOAD = 1024 * 1024 * 15; static final File DATA_DIR = new File(GlobalConfig.DATA_DIR + "/" + GlobalConfig.INFLUENCE_DATA_DIR); /* * Add predefined data via api-upload */ public static int addPredefinedData(SocomRequest req) throws IOException, SQLException, JSONException { try { long uid = req.getUid(); String externalid = req.getCookieVal("id"); String answer = req.getCookieVal("answer").replaceAll(";", ","); String fileext = req.getCookieVal("fileextension"); InputStream is = req.getInputStream(); File f = getInfluenceFile(externalid, fileext); writeFile(f, is); String mimeType = checkMimetype(f); String fileName = getFilenameAndConvertIfNeeded(f, mimeType); // extend answer with filename (regex is ';') answer += ";" + externalid + "/" + fileName; long answerid = db.addPredefinedAnswer(uid, externalid, answer); req.addOutput(JSONUtils.JSONToString(new JSONObject().put("answerid", answerid))); } catch (SocomException e) { return e.getErrorCode(); } return 0; } /* * add free answer data via web interface upload */ public static int addFreeData(SocomRequest req) throws IOException, SQLException, JSONException, FileUploadException { try { Map<String, FileItem> params = req.getMultipartContent(); FileItem messageI = params.get("message"); FileItem influenceIdI = params.get("influenceid"); FileItem dataI = params.get("data"); if(dataI == null) dataI = params.get("Filedata"); // used when drag n drop upload FileItem uidI = params.get("uid"); // FileItem secretI = params.get("secret"); String secretI = ""; //non-null if (messageI == null || influenceIdI == null || dataI == null || uidI == null || secretI == null) { throw new IllegalAccessException(); } String temporaryContentType = dataI.getContentType(); boolean seemsImage = isImageFile(temporaryContentType); boolean seemsAudio = isAudioFile(temporaryContentType); if (!seemsImage && !seemsAudio) { throw new MediaTypeNotSupportedException(temporaryContentType); } long uid = Long.parseLong(uidI.getString()); //FIXME no validation (see /SocomWeb/src/de/tud/kom/socom/web/client/influence/InfluencePresenter.java Line 515) // String secretHash = secretI.getString(); // if(!HSQLUserDatabase.getInstance().validateUser(uid, secretHash)) // throw new UIDOrSecretNotValidException(); String message = messageI.getString().replaceAll(";", ","); String influenceId = influenceIdI.getString(); InputStream data = dataI.getInputStream(); String ending = dataI.getName().substring(dataI.getName().lastIndexOf('.') + 1); File file = getInfluenceFile(influenceId, ending); long maxBytes = db.getMaxUploadSize(influenceId); writeFileWithLimitedSize(file, data, seemsImage, seemsAudio, maxBytes); String mimeType = checkMimetype(file); String fileName = getFilenameAndConvertIfNeeded(file, mimeType); String filePath = influenceId + "/" + fileName; String answer = message + ";" + filePath; long newAnswerId = db.addFreeAnswer(uid, influenceId, answer); req.addOutput(JSONUtils.JSONToString(new JSONObject().put("success", true).put("message", message).put("file", filePath).put("id", newAnswerId))); } catch (SocomException e) { return e.getErrorCode(); } return 0; } /* * helpers to write file */ private static void writeFile(File file, InputStream data) throws FileNotFoundException, IOException { OutputStream fos = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len = -1; while ((len = data.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.close(); } private static void writeFileWithLimitedSize(File file, InputStream data, boolean seemsImage, boolean seemsAudio, long maxBytesPre) throws FileNotFoundException, IOException, IllegalFileSizeException { OutputStream fos = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len = -1; long maxBytes = maxBytesPre != -1 ? maxBytesPre : (seemsImage ? MAX_SIZE_PICTURE_UPLOAD : seemsAudio ? MAX_SIZE_AUDIO_UPLOAD : -1); while ((len = data.read(buffer)) != -1) { fos.write(buffer, 0, len); maxBytes -= len; if (maxBytes < 0) { fos.close(); file.delete(); throw new IllegalFileSizeException(maxBytes + "MB"); } } fos.close(); } /* * get filename, make sure all directories exist */ private static File getInfluenceFile(String externalid, String fileext) { File dir = new File(ResourceLoader.getResource("war_dir") + "/" + DATA_DIR); if (!dir.exists()) dir.mkdir(); dir = new File(dir, externalid); if (!dir.exists()) dir.mkdir(); String[] dataList = dir.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase().startsWith("data_"); } }); int dataCount = dataList.length; File f; while ((f = new File(dir, "data_" + dataCount++ + "." + fileext)).exists()) ; return f; } /* * convert file to acceptable format if needed, return (new) filename */ private static String getFilenameAndConvertIfNeeded(File file, String mimeType) { String fileName = file.getName(); if (isMp3AudioFile(mimeType)) { fileName = convertMp3ToOgg(file); } // add more convertions if needed.. return fileName; } /* * helpers to determine mimetype */ private static String checkMimetype(File file) throws MediaTypeNotSupportedException { String mimeType = getMimetype(file); //TODO JK: determination finds application/octet-stream instead of detailed type.. what to do? (RH) if (!isImageFile(mimeType) && !isAudioFile(mimeType)) { file.delete(); throw new MediaTypeNotSupportedException(mimeType); } return mimeType; } private static String getMimetype(File file) { MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector"); Collection<?> mimeTypes = MimeUtil.getMimeTypes(file); return String.valueOf(mimeTypes); } private static String convertMp3ToOgg(File file) { String newFileName = file.getName().substring(0, file.getName().length() - 4) + ".ogg"; File newFile = new File(file.getParentFile(), newFileName); /* * hotfix for existing files, just append old filename to a incrementing index until filename is free */ int fixExistingFileIndex = 0; while(newFile.exists()) { newFile = new File(file.getParentFile(), fixExistingFileIndex++ + newFileName ); } MediaConverter converter = new FFMPEGConverter(file, newFile); converter.start(); return newFile.getName(); } private static boolean isImageFile(String contentType) { return contentType.equalsIgnoreCase("image/gif") || contentType.equalsIgnoreCase("image/jpeg") || contentType.equalsIgnoreCase("image/png"); } private static boolean isAudioFile(String contentType) { return isOggAudioFile(contentType) || isMp3AudioFile(contentType); } private static boolean isOggAudioFile(String contentType) { return contentType.endsWith("/ogg"); } private static boolean isMp3AudioFile(String contentType) { return contentType.equalsIgnoreCase("audio/mpeg") || contentType.equalsIgnoreCase("audio/x-mpeg") || contentType.equalsIgnoreCase("audio/mp3") || contentType.equalsIgnoreCase("audio/x-mp3") || contentType.equalsIgnoreCase("audio/mpeg3") || contentType.equalsIgnoreCase("audio/x-mpeg3") || contentType.equalsIgnoreCase("audio/mpg") || contentType.equalsIgnoreCase("audio/x-mpg") || contentType.equalsIgnoreCase("audio/x-mpegaudio"); } }