Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. The ASF licenses this file to You * 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. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. */ package org.apache.roller.weblogger.business; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigDecimal; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.roller.weblogger.config.WebloggerConfig; import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.pojos.FileContent; import org.apache.roller.weblogger.pojos.Weblog; import org.apache.roller.weblogger.util.RollerMessages; /** * Manages contents of the file uploaded to Roller weblogs. * * This base implementation writes file content to a file system. */ public class FileContentManagerImpl implements FileContentManager { private static Log log = LogFactory.getLog(FileContentManagerImpl.class); private String storage_dir = null; /** * Create file content manager. */ public FileContentManagerImpl() { String storagedir = WebloggerConfig.getProperty("mediafiles.storage.dir"); // Note: System property expansion is now handled by WebloggerConfig. if (storagedir == null || storagedir.trim().length() < 1) storagedir = System.getProperty("user.home") + File.separator + "roller_data" + File.separator + "mediafiles"; if (!storagedir.endsWith(File.separator)) storagedir += File.separator; this.storage_dir = storagedir.replace('/', File.separatorChar); } public void initialize() { } /** * @see org.apache.roller.weblogger.model.FileContentManager#getFileContent(weblog, java.lang.String) */ public FileContent getFileContent(Weblog weblog, String fileId) throws FileNotFoundException, FilePathException { // get a reference to the file, checks that file exists & is readable File resourceFile = this.getRealFile(weblog, fileId); // make sure file is not a directory if (resourceFile.isDirectory()) { throw new FilePathException("Invalid file id [" + fileId + "], " + "path is a directory."); } // everything looks good, return resource return new FileContent(weblog, fileId, resourceFile); } /** * @see org.apache.roller.weblogger.model.FileContentManager#saveFileContent(weblog, java.lang.String, * java.lang.String, java.io.InputStream, boolean) */ public void saveFileContent(Weblog weblog, String fileId, InputStream is) throws FileNotFoundException, FilePathException, FileIOException { // make sure uploads area exists for this weblog File dirPath = this.getRealFile(weblog, null); // create File that we are about to save File saveFile = new File(dirPath.getAbsolutePath() + File.separator + fileId); byte[] buffer = new byte[8192]; int bytesRead = 0; OutputStream bos = null; try { bos = new FileOutputStream(saveFile); while ((bytesRead = is.read(buffer, 0, 8192)) != -1) { bos.write(buffer, 0, bytesRead); } log.debug("The file has been written to [" + saveFile.getAbsolutePath() + "]"); } catch (Exception e) { throw new FileIOException("ERROR uploading file", e); } finally { try { bos.flush(); bos.close(); } catch (Exception ignored) { } } } /** * @see org.apache.roller.weblogger.model.FileContentManager#deleteFile(weblog, java.lang.String) */ public void deleteFile(Weblog weblog, String fileId) throws FileNotFoundException, FilePathException, FileIOException { // get path to delete file, checks that path exists and is readable File delFile = this.getRealFile(weblog, fileId); if (!delFile.delete()) { log.warn("Delete appears to have failed for [" + fileId + "]"); } } /** * @inheritDoc */ public void deleteAllFiles(Weblog weblog) throws FileIOException { // TODO: Implement } /** * @see org.apache.roller.weblogger.model.FileContentManager#overQuota(weblog) */ public boolean overQuota(Weblog weblog) { String maxDir = WebloggerRuntimeConfig.getProperty("uploads.dir.maxsize"); String maxFile = WebloggerRuntimeConfig.getProperty("uploads.file.maxsize"); BigDecimal maxDirSize = new BigDecimal(maxDir); // in megabytes BigDecimal maxFileSize = new BigDecimal(maxFile); // in megabytes long maxDirBytes = (long) (1024000 * maxDirSize.doubleValue()); try { File storageDir = this.getRealFile(weblog, null); long weblogDirSize = this.getDirSize(storageDir, true); return weblogDirSize > maxDirBytes; } catch (Exception ex) { // shouldn't ever happen, this means user's uploads dir is bad // rethrow as a runtime exception throw new RuntimeException(ex); } } public void release() { } /** * @see org.apache.roller.weblogger.model.FileContentManager#canSave( * weblog, java.lang.String, java.lang.String, long, messages) */ public boolean canSave(Weblog weblog, String fileName, String contentType, long size, RollerMessages messages) { // first check, is uploading enabled? if (!WebloggerRuntimeConfig.getBooleanProperty("uploads.enabled")) { messages.addError("error.upload.disabled"); return false; } // second check, does upload exceed max size for file? BigDecimal maxFileMB = new BigDecimal(WebloggerRuntimeConfig.getProperty("uploads.file.maxsize")); int maxFileBytes = (int) (1024000 * maxFileMB.doubleValue()); log.debug("max allowed file size = " + maxFileBytes); log.debug("attempted save file size = " + size); if (size > maxFileBytes) { String[] args = { fileName, maxFileMB.toString() }; messages.addError("error.upload.filemax", args); return false; } // third check, does file cause weblog to exceed quota? BigDecimal maxDirMB = new BigDecimal(WebloggerRuntimeConfig.getProperty("uploads.dir.maxsize")); long maxDirBytes = (long) (1024000 * maxDirMB.doubleValue()); try { File storageDir = this.getRealFile(weblog, null); long userDirSize = getDirSize(storageDir, true); if (userDirSize + size > maxDirBytes) { messages.addError("error.upload.dirmax", maxDirMB.toString()); return false; } } catch (Exception ex) { // shouldn't ever happen, means the weblogs uploads dir is bad somehow // rethrow as a runtime exception throw new RuntimeException(ex); } // fourth check, is upload type allowed? String allows = WebloggerRuntimeConfig.getProperty("uploads.types.allowed"); String forbids = WebloggerRuntimeConfig.getProperty("uploads.types.forbid"); String[] allowFiles = StringUtils.split(StringUtils.deleteWhitespace(allows), ","); String[] forbidFiles = StringUtils.split(StringUtils.deleteWhitespace(forbids), ","); if (!checkFileType(allowFiles, forbidFiles, fileName, contentType)) { String[] args = { fileName, contentType }; messages.addError("error.upload.forbiddenFile", args); return false; } return true; } /** * Get the size in bytes of given directory. * * Optionally works recursively counting subdirectories if they exist. */ private long getDirSize(File dir, boolean recurse) { long size = 0; if (dir.exists() && dir.isDirectory() && dir.canRead()) { File[] files = dir.listFiles(); long dirSize = 0l; for (int i = 0; i < files.length; i++) { if (!files[i].isDirectory()) { dirSize += files[i].length(); } else if (recurse) { // count a subdirectory dirSize += getDirSize(files[i], recurse); } } size += dirSize; } return size; } /** * Return true if file is allowed to be uplaoded given specified allowed and * forbidden file types. */ private boolean checkFileType(String[] allowFiles, String[] forbidFiles, String fileName, String contentType) { // TODO: Atom Publushing Protocol figure out how to handle file // allow/forbid using contentType. // TEMPORARY SOLUTION: In the allow/forbid lists we will continue to // allow user to specify file extensions (e.g. gif, png, jpeg) but will // now also allow them to specify content-type rules (e.g. */*, image/*, // text/xml, etc.). // if content type is invalid, reject file if (contentType == null || contentType.indexOf("/") == -1) { return false; } // default to false boolean allowFile = false; // if this person hasn't listed any allows, then assume they want // to allow *all* filetypes, except those listed under forbid if (allowFiles == null || allowFiles.length < 1) { allowFile = true; } // First check against what is ALLOWED // check file against allowed file extensions if (allowFiles != null && allowFiles.length > 0) { for (int y = 0; y < allowFiles.length; y++) { // oops, this allowed rule is a content-type, skip it if (allowFiles[y].indexOf("/") != -1) continue; if (fileName.toLowerCase().endsWith(allowFiles[y].toLowerCase())) { allowFile = true; break; } } } // check file against allowed contentTypes if (allowFiles != null && allowFiles.length > 0) { for (int y = 0; y < allowFiles.length; y++) { // oops, this allowed rule is NOT a content-type, skip it if (allowFiles[y].indexOf("/") == -1) continue; if (matchContentType(allowFiles[y], contentType)) { allowFile = true; break; } } } // First check against what is FORBIDDEN // check file against forbidden file extensions, overrides any allows if (forbidFiles != null && forbidFiles.length > 0) { for (int x = 0; x < forbidFiles.length; x++) { // oops, this forbid rule is a content-type, skip it if (forbidFiles[x].indexOf("/") != -1) continue; if (fileName.toLowerCase().endsWith(forbidFiles[x].toLowerCase())) { allowFile = false; break; } } } // check file against forbidden contentTypes, overrides any allows if (forbidFiles != null && forbidFiles.length > 0) { for (int x = 0; x < forbidFiles.length; x++) { // oops, this forbid rule is NOT a content-type, skip it if (forbidFiles[x].indexOf("/") == -1) continue; if (matchContentType(forbidFiles[x], contentType)) { allowFile = false; break; } } } return allowFile; } /** * Super simple contentType range rule matching */ private boolean matchContentType(String rangeRule, String contentType) { if (rangeRule.equals("*/*")) return true; if (rangeRule.equals(contentType)) return true; String ruleParts[] = rangeRule.split("/"); String typeParts[] = contentType.split("/"); if (ruleParts[0].equals(typeParts[0]) && ruleParts[1].equals("*")) return true; return false; } /** * Construct the full real path to a resource in a weblog's uploads area. */ private File getRealFile(Weblog weblog, String fileId) throws FileNotFoundException, FilePathException { // make sure uploads area exists for this weblog File weblogDir = new File(this.storage_dir + weblog.getHandle()); if (!weblogDir.exists()) { weblogDir.mkdirs(); } // now form the absolute path String filePath = weblogDir.getAbsolutePath(); if (fileId != null) { filePath += File.separator + fileId; } // make sure path exists and is readable File file = new File(filePath); if (!file.exists()) { throw new FileNotFoundException("Invalid path [" + filePath + "], " + "directory doesn't exist."); } else if (!file.canRead()) { throw new FilePathException("Invalid path [" + filePath + "], " + "cannot read from path."); } try { // make sure someone isn't trying to sneek outside the uploads dir if (!file.getCanonicalPath().startsWith(weblogDir.getCanonicalPath())) { throw new FilePathException( "Invalid path " + filePath + "], " + "trying to get outside uploads dir."); } } catch (IOException ex) { // rethrow as FilePathException throw new FilePathException(ex); } return file; } }