Java tutorial
/* * CKFinder * ======== * http://ckfinder.com * Copyright (C) 2007-2011, CKSource - Frederico Knabben. All rights reserved. * * The software, this file and its contents are subject to the CKFinder * License. Please read the license.txt file before using, installing, copying, * modifying or distribute this file or part of its contents. The contents of * this file is part of the Source Code of CKFinder. */ package com.kmetop.demsy.modules.ckfinder; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; import org.apache.commons.fileupload.FileUploadBase.IOFileUploadException; import org.apache.commons.fileupload.FileUploadBase.InvalidContentTypeException; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.nutz.lang.Mirror; import com.ckfinder.connector.configuration.Constants; import com.ckfinder.connector.configuration.Events.EventTypes; import com.ckfinder.connector.configuration.IConfiguration; import com.ckfinder.connector.data.AfterFileUploadEventArgs; import com.ckfinder.connector.errors.ConnectorException; import com.ckfinder.connector.errors.ErrorUtils; import com.ckfinder.connector.handlers.command.Command; import com.ckfinder.connector.utils.AccessControlUtil; import com.ckfinder.connector.utils.FileUtils; import com.ckfinder.connector.utils.ImageUtils; import com.ckfinder.connector.utils.PathUtils; import com.kmetop.demsy.Demsy; import com.kmetop.demsy.comlib.LibConst; import com.kmetop.demsy.comlib.biz.field.Upload; import com.kmetop.demsy.comlib.web.IUploadInfo; import com.kmetop.demsy.lang.Dates; import com.kmetop.demsy.log.Log; import com.kmetop.demsy.log.Logs; /** * Class to handle <code>FileUpload</code> command. */ public class FileUploadCommand extends Command { private static Log log = Logs.get(); /** * uploading file name request. */ protected String fileName; /** * file name after rename. */ protected String newFileName; /** * function number to call after file upload compelted. */ protected String ckEditorFuncNum; /** * function number to call after file upload compelted. */ protected String ckFinderFuncNum; /** * connector language. */ private String langCode; /** * flag if file was uploaded correctly. */ protected boolean uploaded; /** * error code number. */ protected int errorCode; private static final char[] UNSAFE_FILE_NAME_CHARS = { ':', '*', '?', '|', '/' }; /** * default constructor. */ public FileUploadCommand() { this.errorCode = 0; this.fileName = ""; this.newFileName = ""; this.type = ""; this.uploaded = false; } /** * exetue file upload command. * * @param out * output strem from response. * @throws ConnectorException * when error occurs. */ @Override public void execute(final OutputStream out) throws ConnectorException { if (configuration.isDebugMode() && this.exception != null) { throw new ConnectorException(this.errorCode, this.exception); } try { String errorMsg = (this.errorCode == 0) ? "" : ErrorUtils.getInstance().getErrorMsgByLangAndCode(this.langCode, this.errorCode, this.configuration); errorMsg = errorMsg.replaceAll("%1", this.newFileName); out.write("<script type=\"text/javascript\">".getBytes("UTF-8")); String path = ""; if (!uploaded) { this.newFileName = ""; this.currentFolder = ""; } else { path = configuration.getTypes().get(type).getUrl() + this.currentFolder; } if (checkFuncNum()) { handleOnUploadCompleteCallFuncResponse(out, errorMsg, path); } else { handleOnUploadCompleteResponse(out, errorMsg); } out.write("</script>".getBytes("UTF-8")); } catch (IOException e) { throw new ConnectorException(Constants.Errors.CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED, e); } } /** * check if func num is set in request. * * @return true if is. */ protected boolean checkFuncNum() { return this.ckFinderFuncNum != null; } /** * return response when func num is set. * * @param out * response. * @param errorMsg * error content * @param path * path * @throws IOException * when error occurs. */ protected void handleOnUploadCompleteCallFuncResponse(final OutputStream out, final String errorMsg, final String path) throws IOException { this.ckFinderFuncNum = this.ckFinderFuncNum.replaceAll("[^\\d]", ""); out.write(("window.parent.CKFinder.tools.callFunction(" + this.ckFinderFuncNum + ", '" + path + this.newFileName + "', '" + errorMsg + "');").getBytes("UTF-8")); } /** * * @param out * out put stream * @param errorMsg * error content * @throws IOException * when error occurs */ protected void handleOnUploadCompleteResponse(final OutputStream out, final String errorMsg) throws IOException { out.write("window.parent.OnUploadCompleted(".getBytes("UTF-8")); out.write(("\'" + this.newFileName + "\'").getBytes("UTF-8")); out.write( (", \'" + (this.errorCode != Constants.Errors.CKFINDER_CONNECTOR_ERROR_NONE ? errorMsg : "") + "\'") .getBytes("UTF-8")); out.write(");".getBytes("UTF-8")); } /** * initializing parametrs for command handler. * * @param request * request * @param configuration * connector configuration. * @param params * execute additional params. * @throws ConnectorException * when error occurs. */ @Override public void initParams(final HttpServletRequest request, final IConfiguration configuration, final Object... params) throws ConnectorException { super.initParams(request, configuration, params); this.ckFinderFuncNum = request.getParameter("CKFinderFuncNum"); this.ckEditorFuncNum = request.getParameter("CKEditorFuncNum"); this.langCode = request.getParameter("langCode"); if (this.errorCode == Constants.Errors.CKFINDER_CONNECTOR_ERROR_NONE) { this.uploaded = uploadFile(request); } } /** * uploads file and saves to file. * * @param request * request * @return true if uploaded correctly. */ private boolean uploadFile(final HttpServletRequest request) { if (!AccessControlUtil.getInstance(configuration).checkFolderACL(this.type, this.currentFolder, this.userRole, AccessControlUtil.CKFINDER_CONNECTOR_ACL_FILE_UPLOAD)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UNAUTHORIZED; return false; } return fileUpload(request); } private void saveToDB(FileItem item, String folder) { IUploadInfo info = (IUploadInfo) Mirror.me(Demsy.bizEngine.getStaticType(LibConst.BIZSYS_ADMIN_UPLOAD)) .born(); Demsy me = Demsy.me(); if (me.getSoft() != null) info.setSoftID(me.getSoft().getId()); info.setContentType(item.getContentType()); info.setExtName("CKFINDER"); info.setLocalName(item.getName()); info.setContentLength(item.getSize()); info.setPath(new Upload(folder + "/" + this.newFileName)); Demsy.orm().save(info); } /** * * @param request * http request * @return true if uploaded correctly */ @SuppressWarnings("unchecked") private boolean fileUpload(final HttpServletRequest request) { try { DiskFileItemFactory fileItemFactory = new DiskFileItemFactory(); ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory); List<FileItem> items = uploadHandler.parseRequest(request); for (FileItem item : items) { String path = configuration.getTypes().get(this.type).getPath() + this.currentFolder; this.fileName = getFileItemName(item); try { if (validateUploadItem(item, path)) { boolean succ = saveTemporaryFile(path, item); try { saveToDB(item, configuration.getTypes().get(this.type).getUrl() + this.currentFolder); } catch (Throwable e) { log.error("??! ", e); } return succ; } } finally { // file should be deleted from temporary files automaticly // but in some cases the file was not deleted // but when file was deleted after item.write // then inputstream isn't available any more. try { item.getOutputStream().close(); item.getInputStream().close(); } catch (IOException e) { // when input stream isn't avail // you don't have to close it // Probably if this issue will be fixed: // https://issues.apache.org/jira/browse/FILEUPLOAD-191 // all code in finally can be deleted. } item.delete(); } } return false; } catch (InvalidContentTypeException e) { if (configuration.isDebugMode()) { this.exception = e; } this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_CORRUPT; return false; } catch (IOFileUploadException e) { if (configuration.isDebugMode()) { this.exception = e; } this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED; return false; } catch (SizeLimitExceededException e) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_TOO_BIG; return false; } catch (FileSizeLimitExceededException e) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_TOO_BIG; return false; } catch (ConnectorException e) { this.errorCode = e.getErrorCode(); return false; } catch (Exception e) { if (configuration.isDebugMode()) { this.exception = e; } this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED; return false; } } /** * saves temporary file in the correct file path. * * @param path * path to save file * @param item * file upload item * @return result of saving, true if saved correctly * @throws Exception * when error occurs. */ private boolean saveTemporaryFile(final String path, final FileItem item) throws Exception { File file = new File(path, this.newFileName); AfterFileUploadEventArgs args = new AfterFileUploadEventArgs(); args.setCurrentFolder(this.currentFolder); args.setFile(file); args.setFileContent(item.get()); if (!ImageUtils.isImage(file)) { item.write(file); if (configuration.getEvents() != null) { configuration.getEvents().run(EventTypes.AfterFileUpload, args, configuration); } return true; } else if (ImageUtils.checkImageSize(item.getInputStream(), this.configuration)) { ImageUtils.createTmpThumb(item.getInputStream(), file, getFileItemName(item), this.configuration); if (configuration.getEvents() != null) { configuration.getEvents().run(EventTypes.AfterFileUpload, args, configuration); } return true; } else if (configuration.checkSizeAfterScaling()) { ImageUtils.createTmpThumb(item.getInputStream(), file, getFileItemName(item), this.configuration); if (FileUtils.checkFileSize(configuration.getTypes().get(this.type), file.length())) { if (configuration.getEvents() != null) { configuration.getEvents().run(EventTypes.AfterFileUpload, args, configuration); } return true; } else { file.delete(); this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_TOO_BIG; return false; } } // should be unreacheable return false; } /** * if file exists this method adds (number) to file. * * @param path * folder * @param name * file name * @return new file name. */ private String getFinalFileName(final String path, final String name) { File file = new File(path, name); int number = 0; while (true) { if (file.exists()) { number++; StringBuilder sb = new StringBuilder(); sb.append(FileUtils.getFileNameWithoutExtension(name)); sb.append("(" + number + ")."); sb.append(FileUtils.getFileExtension(name)); this.newFileName = sb.toString(); file = new File(path, this.newFileName); this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_FILE_RENAMED; } else { return this.newFileName; } } } /** * validates uploaded file. * * @param item * uploaded item. * @param path * file path * @return true if validation */ private boolean validateUploadItem(final FileItem item, final String path) { if (item.getName() != null && item.getName().length() > 0) { this.fileName = getFileItemName(item); } else { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_INVALID; return false; } this.newFileName = this.fileName; String unsafeFileName = this.newFileName; for (char c : UNSAFE_FILE_NAME_CHARS) { this.newFileName = unsafeFileName.replace(c, '_'); } if (configuration.forceASCII()) { this.newFileName = FileUtils.convertToASCII(this.newFileName); } if (!unsafeFileName.equals(this.newFileName)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_INVALID_NAME_RENAMED; } if (FileUtils.checkIfDirIsHidden(this.currentFolder, configuration)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST; return false; } if (!FileUtils.checkFileName(this.newFileName) || FileUtils.checkIfFileIsHidden(this.newFileName, configuration)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_INVALID_NAME; return false; } int checkFileExt = FileUtils.checkFileExtension(this.newFileName, configuration.getTypes().get(type), configuration, true); if (checkFileExt == 1) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_INVALID_EXTENSION; return false; } else if (checkFileExt == 2) { this.newFileName = FileUtils.renameFileWithBadExt(this.newFileName); } try { File file = new File(path, getFinalFileName(path, this.newFileName)); if (!FileUtils.checkFileSize(configuration.getTypes().get(this.type), item.getSize()) && !(configuration.checkSizeAfterScaling() && ImageUtils.isImage(file))) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_TOO_BIG; return false; } if (configuration.getSecureImageUploads() && ImageUtils.isImage(file) && !ImageUtils.checkImageFile(item)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_CORRUPT; return false; } if (!FileUtils.checkIfFileIsHtmlFile(file.getName(), configuration) && FileUtils.detectHtml(item)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_UPLOADED_WRONG_HTML_FILE; return false; } } catch (SecurityException e) { if (configuration.isDebugMode()) { this.exception = e; } this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED; return false; } catch (IOException e) { if (configuration.isDebugMode()) { this.exception = e; } this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED; return false; } return true; } /** * set response headers. Not user in this command. * * @param response * response * @param sc * servlet context */ @Override public void setResponseHeader(final HttpServletResponse response, final ServletContext sc) { response.setCharacterEncoding("utf-8"); response.setContentType("text/html"); } /** * save if uploaded file item name is full file path not only file name. * * @param item * file upload item * @return file name of uploaded item */ private String getFileItemName(final FileItem item) { Pattern p = Pattern.compile("[^\\\\/]+$"); Matcher m = p.matcher(item.getName()); String filename = (m.find()) ? m.group(0) : ""; // String ext = ""; // // int dot = filename.lastIndexOf("."); // if (dot > 0) { // ext = filename.substring(dot); // filename = filename.substring(0, dot); // } // // return Integer.toHexString(filename.hashCode()) + ext; return filename; } /** * check request for security issue. * * @param reqParam * request param * @return true if validation passed * @throws ConnectorException * if valdation error occurs. */ protected boolean checkParam(final String reqParam) throws ConnectorException { if (reqParam == null || reqParam.equals("")) { return true; } if (Pattern.compile(Constants.INVALID_PATH_REGEX).matcher(reqParam).find()) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_INVALID_NAME; return false; } return true; } @Override protected boolean checkHidden() throws ConnectorException { if (FileUtils.checkIfDirIsHidden(this.currentFolder, configuration)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST; return true; } return false; } @Override protected boolean checkConnector(final HttpServletRequest request) throws ConnectorException { if (!configuration.enabled() || !configuration.checkAuthentication(request)) { this.errorCode = Constants.Errors.CKFINDER_CONNECTOR_ERROR_CONNECTOR_DISABLED; return false; } return true; } @Override protected boolean checkIfCurrFolderExists(final HttpServletRequest request) throws ConnectorException { String tmpType = getParameter(request, "type"); File currDir = new File(configuration.getTypes().get(tmpType).getPath() + this.currentFolder); if (currDir.exists() && currDir.isDirectory()) { return true; } else { currDir.mkdirs(); return true; // this.errorCode = // Constants.Errors.CKFINDER_CONNECTOR_ERROR_FOLDER_NOT_FOUND; // return false; } } protected void getCurrentFolderParam(final HttpServletRequest request) { String currFolder = getParameter(request, "currentFolder"); if (currFolder == null || currFolder.equals("") || currFolder.equals("/")) { this.currentFolder = "/" + Dates.formatDate(new Date(), "yyMM") + "/"; } else { this.currentFolder = PathUtils.addSlashToBeginning(PathUtils.addSlashToEnd(currFolder)); } } }