1 31 package org.blojsom.plugin.admin; 32 33 import org.apache.commons.fileupload.FileItem; 34 import org.apache.commons.fileupload.FileUploadException; 35 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 36 import org.apache.commons.fileupload.servlet.ServletFileUpload; 37 import org.apache.commons.logging.Log; 38 import org.apache.commons.logging.LogFactory; 39 import org.blojsom.blog.Blog; 40 import org.blojsom.blog.Entry; 41 import org.blojsom.plugin.PluginException; 42 import org.blojsom.util.BlojsomConstants; 43 import org.blojsom.util.BlojsomUtils; 44 45 import javax.servlet.http.HttpServletRequest ; 46 import javax.servlet.http.HttpServletResponse ; 47 import java.io.File ; 48 import java.util.*; 49 50 57 public class FileUploadPlugin extends BaseAdminPlugin { 58 59 private Log _logger = LogFactory.getLog(FileUploadPlugin.class); 60 61 private static final String FAILED_PERMISSION_KEY = "file.upload.failed.permission.text"; 63 private static final String FAILED_RESOURCE_KEY = "file.upload.failed.resource.text"; 64 private static final String UNKNOWN_ERROR_KEY = "file.upload.unknown.error.text"; 65 private static final String SUCCESSFUL_UPLOAD_KEY = "successful.upload.text"; 66 private static final String INVALID_EXTENSION_KEY = "invalid.upload.extension.text"; 67 private static final String INVALID_TYPE_KEY = "invalid.upload.type.text"; 68 private static final String DELETED_FILES_KEY = "deleted.files.text"; 69 private static final String UPLOAD_LIMIT_KEY = "upload.limit.exceeded.text"; 70 71 private static final String RESOURCES_DIRECTORY_IP = "resources-directory"; 72 private static final String TEMPORARY_DIRECTORY_IP = "temporary-directory"; 73 private static final String DEFAULT_TEMPORARY_DIRECTORY = "/tmp"; 74 private static final String MAXIMUM_UPLOAD_SIZE_IP = "maximum-upload-size"; 75 private static final long DEFAULT_MAXIMUM_UPLOAD_SIZE = 100000; 76 private static final String MAXIMUM_MEMORY_SIZE_IP = "maximum-memory-size"; 77 private static final int DEFAULT_MAXIMUM_MEMORY_SIZE = 50000; 78 private static final String ACCEPTED_FILE_TYPES_IP = "accepted-file-types"; 79 private static final String [] DEFAULT_ACCEPTED_FILE_TYPES = {"image/jpeg", "image/gif", "image/png"}; 80 private static final String INVALID_FILE_EXTENSIONS_IP = "invalid-file-extensions"; 81 private static final String [] DEFAULT_INVALID_FILE_EXTENSIONS = {".jsp", ".jspf", ".jspi", ".jspx", ".php", ".cgi"}; 82 private static final String UPLOAD_QUOTA_ENABLED_IP = "upload-quota-enabled"; 83 private static final String UPLOAD_QUOTA_LIMIT_IP = "upload-quota-limit"; 84 private static final long DEFAULT_UPLOAD_QUOTA_LIMIT = 10485760; 85 86 private static final String FILE_UPLOAD_PAGE = "/org/blojsom/plugin/admin/templates/admin-file-upload"; 88 89 private static final String PLUGIN_ADMIN_FILE_UPLOAD_FILES = "PLUGIN_ADMIN_FILE_UPLOAD_FILES"; 91 private static final String ACCEPTED_FILE_TYPES = "ACCEPTED_FILE_TYPES"; 92 private static final String INVALID_FILE_EXTENSIONS = "INVALID_FILE_EXTENSIONS"; 93 94 private static final String UPLOAD_FILE_ACTION = "upload-file"; 96 private static final String DELETE_UPLOAD_FILES = "delete-upload-files"; 97 98 private static final String FILE_TO_DELETE = "file-to-delete"; 100 101 private static final String FILE_UPLOAD_PERMISSION = "file_upload_permission"; 103 104 private String _temporaryDirectory; 105 private long _maximumUploadSize; 106 private int _maximumMemorySize; 107 private Map _acceptedFileTypes; 108 private String _resourcesDirectory; 109 private String [] _invalidFileExtensions; 110 private boolean _uploadQuotaEnabled; 111 private long _uploadQuotaLimit; 112 private Properties _fileUploadProperties; 113 114 117 public FileUploadPlugin() { 118 } 119 120 125 public void setFileUploadProperties(Properties fileUploadProperties) { 126 _fileUploadProperties = fileUploadProperties; 127 } 128 129 135 public void init() throws PluginException { 136 super.init(); 137 138 _temporaryDirectory = _fileUploadProperties.getProperty(TEMPORARY_DIRECTORY_IP); 139 if (BlojsomUtils.checkNullOrBlank(_temporaryDirectory)) { 140 _temporaryDirectory = DEFAULT_TEMPORARY_DIRECTORY; 141 } 142 _logger.debug("Using temporary directory: " + _temporaryDirectory); 143 144 try { 145 _maximumUploadSize = Long.parseLong(_fileUploadProperties.getProperty(MAXIMUM_UPLOAD_SIZE_IP)); 146 } catch (NumberFormatException e) { 147 _maximumUploadSize = DEFAULT_MAXIMUM_UPLOAD_SIZE; 148 } 149 _logger.debug("Using maximum upload size: " + _maximumUploadSize); 150 151 try { 152 _maximumMemorySize = Integer.parseInt(_fileUploadProperties.getProperty(MAXIMUM_MEMORY_SIZE_IP)); 153 } catch (NumberFormatException e) { 154 _maximumMemorySize = DEFAULT_MAXIMUM_MEMORY_SIZE; 155 } 156 _logger.debug("Using maximum memory size: " + _maximumMemorySize); 157 158 String acceptedFileTypes = _fileUploadProperties.getProperty(ACCEPTED_FILE_TYPES_IP); 159 String [] parsedListOfTypes; 160 if (BlojsomUtils.checkNullOrBlank(acceptedFileTypes)) { 161 parsedListOfTypes = DEFAULT_ACCEPTED_FILE_TYPES; 162 } else { 163 parsedListOfTypes = BlojsomUtils.parseCommaList(acceptedFileTypes); 164 } 165 _acceptedFileTypes = new HashMap(parsedListOfTypes.length); 166 for (int i = 0; i < parsedListOfTypes.length; i++) { 167 String type = parsedListOfTypes[i]; 168 _acceptedFileTypes.put(type, type); 169 } 170 _logger.debug("Using accepted file types: " + BlojsomUtils.arrayOfStringsToString(parsedListOfTypes)); 171 172 _resourcesDirectory = _fileUploadProperties.getProperty(RESOURCES_DIRECTORY_IP); 173 if (BlojsomUtils.checkNullOrBlank(_resourcesDirectory)) { 174 _resourcesDirectory = BlojsomConstants.DEFAULT_RESOURCES_DIRECTORY; 175 } 176 177 _resourcesDirectory = BlojsomUtils.checkStartingAndEndingSlash(_resourcesDirectory); 178 _logger.debug("Using resources directory: " + _resourcesDirectory); 179 180 String invalidFileExtensionsProperty = _fileUploadProperties.getProperty(INVALID_FILE_EXTENSIONS_IP); 181 if (BlojsomUtils.checkNullOrBlank(invalidFileExtensionsProperty)) { 182 _invalidFileExtensions = DEFAULT_INVALID_FILE_EXTENSIONS; 183 } else { 184 _invalidFileExtensions = BlojsomUtils.parseCommaList(invalidFileExtensionsProperty); 185 } 186 _logger.debug("Using invalid file extensions: " + invalidFileExtensionsProperty); 187 188 _uploadQuotaEnabled = Boolean.valueOf(_fileUploadProperties.getProperty(UPLOAD_QUOTA_ENABLED_IP)).booleanValue(); 189 if (_uploadQuotaEnabled) { 190 String uploadQuotaLimit = _fileUploadProperties.getProperty(UPLOAD_QUOTA_LIMIT_IP); 191 if (BlojsomUtils.checkNullOrBlank(uploadQuotaLimit)) { 192 _uploadQuotaLimit = DEFAULT_UPLOAD_QUOTA_LIMIT; 193 } else { 194 try { 195 _uploadQuotaLimit = Long.parseLong(uploadQuotaLimit); 196 if (_uploadQuotaLimit <= 0) { 197 _uploadQuotaLimit = DEFAULT_UPLOAD_QUOTA_LIMIT; 198 } 199 } catch (NumberFormatException e) { 200 _uploadQuotaLimit = DEFAULT_UPLOAD_QUOTA_LIMIT; 201 } 202 } 203 204 _logger.debug("Upload limit enabled. Quota is : " + _uploadQuotaLimit + " bytes"); 205 } 206 } 207 208 214 protected long getDirectorySize(File directory) { 215 if (!directory.isDirectory()) { 216 return -1; 217 } 218 219 long totalSize = 0; 220 221 File [] files = directory.listFiles(); 222 for (int i = 0; i < files.length; i++) { 223 File file = files[i]; 224 225 if (file.isDirectory()) { 226 totalSize += getDirectorySize(file); 227 } else { 228 totalSize += file.length(); 229 } 230 } 231 232 return totalSize; 233 } 234 235 246 public Entry[] process(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Blog blog, Map context, Entry[] entries) throws PluginException { 247 if (!authenticateUser(httpServletRequest, httpServletResponse, context, blog)) { 248 httpServletRequest.setAttribute(BlojsomConstants.PAGE_PARAM, ADMIN_LOGIN_PAGE); 249 250 return entries; 251 } 252 253 String username = getUsernameFromSession(httpServletRequest, blog); 254 if (!checkPermission(blog, null, username, FILE_UPLOAD_PERMISSION)) { 255 httpServletRequest.setAttribute(BlojsomConstants.PAGE_PARAM, ADMIN_ADMINISTRATION_PAGE); 256 addOperationResultMessage(context, getAdminResource(FAILED_PERMISSION_KEY, FAILED_PERMISSION_KEY, blog.getBlogAdministrationLocale())); 257 258 return entries; 259 } 260 261 File resourceDirectory = new File (_servletConfig.getServletContext().getRealPath("/") + _resourcesDirectory + blog.getBlogId() + "/"); 262 263 String action = BlojsomUtils.getRequestValue(ACTION_PARAM, httpServletRequest); 264 if (BlojsomUtils.checkNullOrBlank(action)) { 265 _logger.debug("User did not request edit action"); 266 267 httpServletRequest.setAttribute(BlojsomConstants.PAGE_PARAM, ADMIN_ADMINISTRATION_PAGE); 268 } else if (PAGE_ACTION.equals(action)) { 269 _logger.debug("User requested file upload page"); 270 271 httpServletRequest.setAttribute(BlojsomConstants.PAGE_PARAM, FILE_UPLOAD_PAGE); 272 } else if (UPLOAD_FILE_ACTION.equals(action)) { 273 _logger.debug("User requested file upload action"); 274 275 DiskFileItemFactory factory = new DiskFileItemFactory(); 277 278 factory.setSizeThreshold(_maximumMemorySize); 280 factory.setRepository(new File (_temporaryDirectory)); 281 282 ServletFileUpload upload = new ServletFileUpload(factory); 283 284 upload.setSizeMax(_maximumUploadSize); 286 287 try { 288 List items = upload.parseRequest(httpServletRequest); 289 Iterator itemsIterator = items.iterator(); 290 while (itemsIterator.hasNext()) { 291 FileItem item = (FileItem) itemsIterator.next(); 292 293 if (!item.isFormField()) { 295 String itemNameWithoutPath = BlojsomUtils.getFilenameFromPath(item.getName()); 296 297 _logger.debug("Found file item: " + itemNameWithoutPath + " of type: " + item.getContentType()); 298 299 String fileType = item.getContentType(); 301 boolean isAcceptedFileType = _acceptedFileTypes.containsKey(fileType); 302 303 String extension = BlojsomUtils.getFileExtension(itemNameWithoutPath); 304 boolean isAcceptedFileExtension = true; 305 for (int i = 0; i < _invalidFileExtensions.length; i++) { 306 String invalidFileExtension = _invalidFileExtensions[i]; 307 if (itemNameWithoutPath.indexOf(invalidFileExtension) != -1) { 308 isAcceptedFileExtension = false; 309 break; 310 } 311 } 312 313 if (_uploadQuotaEnabled) { 314 boolean overQuota = true; 315 long currentLimit = getDirectorySize(resourceDirectory); 316 if ((currentLimit != -1) && ((currentLimit + item.getSize() < _uploadQuotaLimit))) { 317 overQuota = false; 318 } 319 320 if (overQuota) { 321 _logger.error("Upload quota exceeded trying to upload file: " + itemNameWithoutPath); 322 addOperationResultMessage(context, formatAdminResource(UPLOAD_LIMIT_KEY, UPLOAD_LIMIT_KEY, blog.getBlogAdministrationLocale(), new Object []{itemNameWithoutPath, new Long (_uploadQuotaLimit)})); 323 324 break; 325 } 326 } 327 328 if (isAcceptedFileType && isAcceptedFileExtension) { 330 if (!resourceDirectory.exists()) { 331 if (!resourceDirectory.mkdirs()) { 332 _logger.error("Unable to create resource directory for user: " + resourceDirectory.toString()); 333 addOperationResultMessage(context, getAdminResource(FAILED_RESOURCE_KEY, FAILED_RESOURCE_KEY, blog.getBlogAdministrationLocale())); 334 return entries; 335 } 336 } 337 338 File resourceFile = new File (resourceDirectory, itemNameWithoutPath); 339 try { 340 item.write(resourceFile); 341 } catch (Exception e) { 342 _logger.error(e); 343 addOperationResultMessage(context, formatAdminResource(UNKNOWN_ERROR_KEY, UNKNOWN_ERROR_KEY, blog.getBlogAdministrationLocale(), new Object []{e.getMessage()})); 344 } 345 346 String resourceURL = blog.getBlogBaseURL() + _resourcesDirectory + blog.getBlogId() + "/" + itemNameWithoutPath; 347 348 _logger.debug("Successfully uploaded resource file: " + resourceFile.toString()); 349 addOperationResultMessage(context, formatAdminResource(SUCCESSFUL_UPLOAD_KEY, SUCCESSFUL_UPLOAD_KEY, blog.getBlogAdministrationLocale(), new Object []{itemNameWithoutPath, resourceURL, itemNameWithoutPath})); 350 } else { 351 if (!isAcceptedFileExtension) { 352 _logger.error("Upload file does not have an accepted extension: " + extension); 353 addOperationResultMessage(context, formatAdminResource(INVALID_EXTENSION_KEY, INVALID_EXTENSION_KEY, blog.getBlogAdministrationLocale(), new Object []{extension})); 354 } else { 355 _logger.error("Upload file is not an accepted type: " + itemNameWithoutPath + " of type: " + item.getContentType()); 356 addOperationResultMessage(context, formatAdminResource(INVALID_TYPE_KEY, INVALID_TYPE_KEY, blog.getBlogAdministrationLocale(), new Object []{item.getContentType()})); 357 } 358 } 359 } 360 } 361 } catch (FileUploadException e) { 362 _logger.error(e); 363 addOperationResultMessage(context, formatAdminResource(UNKNOWN_ERROR_KEY, UNKNOWN_ERROR_KEY, blog.getBlogAdministrationLocale(), new Object []{e.getMessage()})); 364 } 365 366 httpServletRequest.setAttribute(BlojsomConstants.PAGE_PARAM, FILE_UPLOAD_PAGE); 367 } else if (DELETE_UPLOAD_FILES.equals(action)) { 368 String [] filesToDelete = httpServletRequest.getParameterValues(FILE_TO_DELETE); 369 int actualFilesDeleted = 0; 370 if (filesToDelete != null && filesToDelete.length > 0) { 371 File deletedFile; 372 for (int i = 0; i < filesToDelete.length; i++) { 373 String fileToDelete = filesToDelete[i]; 374 deletedFile = new File (resourceDirectory, fileToDelete); 375 if (!deletedFile.delete()) { 376 _logger.debug("Unable to delete resource file: " + deletedFile.toString()); 377 } else { 378 actualFilesDeleted += 1; 379 } 380 } 381 382 addOperationResultMessage(context, formatAdminResource(DELETED_FILES_KEY, DELETED_FILES_KEY, blog.getBlogAdministrationLocale(), new Object []{new Integer (actualFilesDeleted)})); 383 } 384 385 httpServletRequest.setAttribute(BlojsomConstants.PAGE_PARAM, FILE_UPLOAD_PAGE); 386 } 387 388 Map resourceFilesMap = null; 390 if (resourceDirectory.exists()) { 391 File [] resourceFiles = resourceDirectory.listFiles(); 392 393 if (resourceFiles != null) { 394 resourceFilesMap = new HashMap(resourceFiles.length); 395 for (int i = 0; i < resourceFiles.length; i++) { 396 File resourceFile = resourceFiles[i]; 397 resourceFilesMap.put(resourceFile.getName(), resourceFile.getName()); 398 } 399 } 400 } else { 401 resourceFilesMap = new HashMap(); 402 } 403 404 resourceFilesMap = new TreeMap(resourceFilesMap); 405 context.put(PLUGIN_ADMIN_FILE_UPLOAD_FILES, resourceFilesMap); 406 context.put(ACCEPTED_FILE_TYPES, new TreeMap(_acceptedFileTypes)); 407 context.put(INVALID_FILE_EXTENSIONS, new TreeMap(BlojsomUtils.listToMap(BlojsomUtils.arrayToList(_invalidFileExtensions)))); 408 409 return entries; 410 } 411 } 412 | Popular Tags |