diff --git a/README.md b/README.md
index 542ac80..fc73697 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,8 @@
This is just a spare-time project. The usage of this tool (especially in production systems) is at your own risk.
-Last Release: 1.1.6.1 - 18.01.2018
+Prev Release: 1.1.6.1 - 18.01.2018
+Last Release: 1.1.6.2 - 20.03.2018
[![Maven Central](https://img.shields.io/maven-central/v/de.chandre.admin-tools/admin-tools-core.svg)](https://mvnrepository.com/artifact/de.chandre.admin-tools)
[![GitHub issues](https://img.shields.io/github/issues/andrehertwig/admintool.svg)](https://github.com/andrehertwig/admintool/issues)
@@ -79,7 +80,7 @@ Include the dependencies in your dependency management. You can find it in [Mave
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
...
```
diff --git a/admin-tools-core/README.md b/admin-tools-core/README.md
index 5cfc9e8..4412a67 100644
--- a/admin-tools-core/README.md
+++ b/admin-tools-core/README.md
@@ -20,7 +20,7 @@
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-dbbrowser/README.md b/admin-tools-dbbrowser/README.md
index d8d36c8..cdb3736 100644
--- a/admin-tools-dbbrowser/README.md
+++ b/admin-tools-dbbrowser/README.md
@@ -32,12 +32,12 @@ Result will be displayed via jquery.datatables
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-dbbrowser
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-filebrowser/README.md b/admin-tools-filebrowser/README.md
index a8df86d..ec5e6ea 100644
--- a/admin-tools-filebrowser/README.md
+++ b/admin-tools-filebrowser/README.md
@@ -34,12 +34,12 @@
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-filebrowser
- 1.1.6.1
+ 1.1.6.2
```
@@ -118,6 +118,10 @@ admintool.filebrowser.createFolderAllowed=false
# boolean: to enable action to delete folders
admintool.filebrowser.delteFolderAllowed=false
+#since 1.1.6.2
+# boolean: if set to true and file/folder to delete has no write permission, it's not allowed to delete them
+admintool.filebrowser.notDeletableIfNotWriteable=true
+
#since 1.1.6
# boolean: to enable action to delete files
admintool.filebrowser.delteFileAllowed=false
diff --git a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AbstractFileBrowserService.java b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AbstractFileBrowserService.java
index 7545395..5e54071 100644
--- a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AbstractFileBrowserService.java
+++ b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AbstractFileBrowserService.java
@@ -1,63 +1,74 @@
-package de.chandre.admintool.filebrowser;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.Map;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- *
- * @author Andre
- *
- */
-public abstract class AbstractFileBrowserService {
-
- @Autowired
- private AdminToolFilebrowserConfig config;
-
- public String encodeURL(String path) throws UnsupportedEncodingException {
- return URLEncoder.encode(path, "UTF-8");
- }
-
- /**
- *
- * @param path
- * @param write
- * @param configReadOnly
- * @return
- * @throws IOException
- */
- public boolean isAllowed(File path, boolean write, boolean configReadOnly) throws IOException {
- try {
- if (configReadOnly && write) return false;
- if (config.isRestrictedBrowsing()) {
- if (null != config.getRestrictedPaths() && null != path) {
- for (String restricedPath : config.getRestrictedPaths()) {
- if(path.getCanonicalPath().startsWith(restricedPath)) {
- return config.isRestrictedBrowsingIsWhitelist();
- }
- }
- }
- return !config.isRestrictedBrowsingIsWhitelist();
- }
- } catch (IOException e) {
- throw new IOException("Could not check if path '" + path.getAbsolutePath() + "' is allowed ", e);
- }
- return true;
- }
-
- public String getExtension(File file) {
- return getExtension(file.getName());
- }
-
- public String getExtension(String fileName) {
- if (fileName.lastIndexOf('.') > -1) {
- return (fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length())).toLowerCase();
- }
- return null;
- }
-
-}
+package de.chandre.admintool.filebrowser;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ *
+ * @author Andre
+ *
+ */
+public abstract class AbstractFileBrowserService {
+
+ @Autowired
+ private AdminToolFilebrowserConfig config;
+
+ public String encodeURL(String path) throws UnsupportedEncodingException {
+ return URLEncoder.encode(path, "UTF-8");
+ }
+
+ /**
+ * checks file against configured path restrictions and read-write configuration
+ * doesn't check file permissions
+ *
+ * @param path the file to show/manipulate
+ * @param write if current action want to change the file
+ * @param configReadOnly if configuration value read-only is true
+ * @return true if accessible
+ * @throws IOException
+ */
+ public boolean isAllowed(File path, boolean write, boolean configReadOnly) throws IOException {
+ try {
+ if (configReadOnly && write) return false;
+ if (config.isRestrictedBrowsing()) {
+ if (null != config.getRestrictedPaths() && null != path) {
+ for (String restricedPath : config.getRestrictedPaths()) {
+ if(path.getCanonicalPath().startsWith(restricedPath)) {
+ return config.isRestrictedBrowsingIsWhitelist();
+ }
+ }
+ }
+ return !config.isRestrictedBrowsingIsWhitelist();
+ }
+ } catch (IOException e) {
+ throw new IOException("Could not check if path '" + path.getAbsolutePath() + "' is allowed ", e);
+ }
+ return true;
+ }
+
+ /**
+ * returns the file extension by filename separated by last dot
+ * @param file
+ * @return null or extension
+ */
+ public String getExtension(File file) {
+ return getExtension(file.getName());
+ }
+
+ /**
+ * returns the file extension by filename separated by last dot
+ * @param fileName
+ * @return null or extension
+ */
+ public String getExtension(String fileName) {
+ if (fileName.lastIndexOf('.') > -1) {
+ return (fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length())).toLowerCase();
+ }
+ return null;
+ }
+
+}
diff --git a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserConfig.java b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserConfig.java
index d6cbb29..79d092d 100644
--- a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserConfig.java
+++ b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserConfig.java
@@ -1,409 +1,421 @@
-package de.chandre.admintool.filebrowser;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.PostConstruct;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-
-import de.chandre.admintool.core.AdminToolConfig;
-
-/**
- * configuration class for file uploader
- *
- * @author André
- * @since 1.0.0
- */
-@Component("adminToolFilebrowserConfig")
-public class AdminToolFilebrowserConfig implements AdminToolConfig {
-
- private static final Log LOGGER = LogFactory.getLog(AdminToolFilebrowserConfig.class);
-
- @Value("${admintool.filebrowser.enabled:true}")
- private boolean enabled;
-
- @Value("${admintool.filebrowser.hideMenuItem:false}")
- private boolean hideMenuItem;
-
- @Value("${admintool.filebrowser.startDir:}")
- private String startDir;
-
- @Value("#{'${admintool.filebrowser.forbiddenDrives:}'.split(';')}")
- private List forbiddenDrives= new ArrayList<>();
-
- @Value("${admintool.filebrowser.readOnly:false}")
- private boolean readOnly;
-
- @Value("${admintool.filebrowser.restrictedBrowsing:false}")
- private boolean restrictedBrowsing;
-
- @Value("${admintool.filebrowser.restrictedBrowsingIsWhitelist:true}")
- private boolean restrictedBrowsingIsWhitelist;
-
- @Value("#{'${admintool.filebrowser.restrictedPaths:}'.split(';')}")
- private List restrictedPaths = new ArrayList<>();
-
- @Value("${admintool.filebrowser.sizeDivisorMultiplicator:1000}")
- private long sizeDivisorMultiplicator;
-
- @Value("${admintool.filebrowser.fileSizeDisplayScale:2}")
- private byte fileSizeDisplayScale;
-
- @Value("${admintool.filebrowser.zipUseTempFile:true}")
- private boolean zipUseTempFile;
-
- @Value("${admintool.filebrowser.zipCompessionLevel:1}")
- private byte zipCompessionLevel;
-
- @Value("${admintool.filebrowser.zipTempDir:'sys:java.io.tmpdir'}")
- private String zipTempDir;
-
- @Value("${admintool.filebrowser.downloadAllowed:true}")
- private boolean downloadAllowed;
-
- @Value("${admintool.filebrowser.downloadCompressedAllowed:true}")
- private boolean downloadCompressedAllowed;
-
- @Value("#{'${admintool.filebrowser.securityRoles:}'.split(';')}")
- private Set securityRoles = new HashSet<>();
-
- @Value("${admintool.filebrowser.componentPosition:}")
- private Integer componentPosition;
-
- @Value("${admintool.filebrowser.uploadAllowed:false}")
- private boolean uploadAllowed;
-
- @Value("${admintool.filebrowser.createFolderAllowed:false}")
- private boolean createFolderAllowed;
-
- @Value("${admintool.filebrowser.delteFolderAllowed:false}")
- private boolean delteFolderAllowed;
-
- @Value("${admintool.filebrowser.delteFileAllowed:false}")
- private boolean delteFileAllowed;
-
- @Value("${admintool.filebrowser.info.crc32:true}")
- private boolean infoCrc32;
-
- @Value("${admintool.filebrowser.info.md5:true}")
- private boolean infoMD5;
-
- @Value("${admintool.filebrowser.info.sha1:true}")
- private boolean infoSha1;
-
- @Value("${admintool.filebrowser.info.sha256:false}")
- private boolean infoSha256;
-
- @Value("${admintool.filebrowser.info.maxFilesizeForHashes:1000000000}")
- private long maxFilesizeForHashes;
-
- @Value("${admintool.filebrowser.info.countFolderSize:true}")
- private boolean countFolderSize;
-
- @Override
- public boolean isEnabled() {
- return this.enabled;
- }
-
- /**
- * @return the hideMenuItem
- */
- public boolean isHideMenuItem() {
- return hideMenuItem;
- }
-
- /**
- * @return the startDir
- */
- public File getStartDir() {
- if (StringUtils.isEmpty(this.startDir)) {
- return new File(this.getClass().getClassLoader().getResource("").getPath());
- }
- File start = new File(this.startDir);
- return start.isFile() ? start.getParentFile() : start;
- }
-
- /**
- * @return the forbiddenDrives
- */
- public List getForbiddenDrives() {
- return forbiddenDrives;
- }
-
- /**
- * @return the readOnly
- */
- public boolean isReadOnly() {
- return isEnabled() && readOnly;
- }
-
- /**
- * @return the restrictBrowsing
- */
- public boolean isRestrictedBrowsing() {
- return restrictedBrowsing;
- }
-
- /**
- * @return the restrictedBrowsingIsWhitelist
- */
- public boolean isRestrictedBrowsingIsWhitelist() {
- return restrictedBrowsingIsWhitelist;
- }
-
- /**
- * @return the restrictedPaths
- */
- public List getRestrictedPaths() {
- return restrictedPaths;
- }
-
- /**
- * @return the sizeDivisorMultiplicator
- */
- public long getSizeDivisorMultiplicator() {
- return sizeDivisorMultiplicator;
- }
-
- /**
- * @return the sizeDivisorMultiplicator
- */
- public void setSizeDivisorMultiplicator(long sizeDivisorMultiplicator) {
- this.sizeDivisorMultiplicator = sizeDivisorMultiplicator;
- }
-
- /**
- * @return the fileSizeDisplayScale
- */
- public byte getFileSizeDisplayScale() {
- return fileSizeDisplayScale;
- }
-
- public void setFileSizeDisplayScale(byte fileSizeDisplayScale) {
- this.fileSizeDisplayScale = fileSizeDisplayScale;
- }
-
- /**
- * @return the zipUseTempFile
- */
- public boolean isZipUseTempFile() {
- return zipUseTempFile;
- }
-
- /**
- * @return the zipTempDir
- */
- public String getZipTempDir() {
- if (this.zipTempDir.charAt(0) == '\''){
- return zipTempDir.substring(1).substring(0, this.zipTempDir.length() -2);
- }
- return zipTempDir;
- }
-
- /**
- * @return the zipCompessionLevel
- */
- public byte getZipCompessionLevel() {
- return zipCompessionLevel;
- }
-
- /**
- * @return the downloadAllowed
- */
- public boolean isDownloadAllowed() {
- return isEnabled() && downloadAllowed;
- }
-
- /**
- *
- * @return
- * @since 1.0.6
- */
- public boolean isDownloadCompressedAllowed() {
- return isEnabled() && downloadCompressedAllowed;
- }
-
- /**
- * @return the securityRoles
- * @since 1.0.1
- */
- public Set getSecurityRoles() {
- return securityRoles;
- }
-
- /**
- * @return the componentPosition
- * @since 1.0.1
- */
- public Integer getComponentPosition() {
- return componentPosition;
- }
-
- /**
- * @param componentPosition the componentPosition to set
- * @since 1.0.1
- */
- public void setComponentPosition(Integer componentPosition) {
- this.componentPosition = componentPosition;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isManipulationAllowed() {
- return !readOnly && (uploadAllowed || createFolderAllowed);
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isUploadAllowed() {
- return isEnabled() && uploadAllowed;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isCreateFolderAllowed() {
- return isEnabled() && createFolderAllowed;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isDelteFolderAllowed() {
- return isEnabled() && delteFolderAllowed;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isDelteFileAllowed() {
- return isEnabled() && delteFileAllowed;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isInfoCrc32() {
- return infoCrc32;
- }
-
- public void setInfoCrc32(boolean infoCrc32) {
- this.infoCrc32 = infoCrc32;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isInfoMD5() {
- return infoMD5;
- }
-
- public void setInfoMD5(boolean infoMD5) {
- this.infoMD5 = infoMD5;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isInfoSha1() {
- return infoSha1;
- }
-
- public void setInfoSha1(boolean infoSha1) {
- this.infoSha1 = infoSha1;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isInfoSha256() {
- return infoSha256;
- }
-
- public void setInfoSha256(boolean infoSha256) {
- this.infoSha256 = infoSha256;
- }
-
- /**
- *
- * @return
- * @since 1.0.6
- */
- public long getMaxFilesizeForHashes() {
- return maxFilesizeForHashes;
- }
-
- public void setMaxFilesizeForHashes(long maxFilesizeForHashes) {
- this.maxFilesizeForHashes = maxFilesizeForHashes;
- }
-
- /**
- *
- * @return
- * @since 1.1.6
- */
- public boolean isCountFolderSize() {
- return countFolderSize;
- }
-
- public void setCountFolderSize(boolean countFolderSize) {
- this.countFolderSize = countFolderSize;
- }
-
- @Override
- @PostConstruct
- public void printConfig() {
- LOGGER.debug(toString());
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("AdminToolFilebrowserConfig [enabled=").append(enabled).append(", hideMenuItem=")
- .append(hideMenuItem).append(", startDir=").append(startDir).append(", forbiddenDrives=")
- .append(forbiddenDrives).append(", readOnly=").append(readOnly).append(", restrictedBrowsing=")
- .append(restrictedBrowsing).append(", restrictedBrowsingIsWhitelist=")
- .append(restrictedBrowsingIsWhitelist).append(", restrictedPaths=").append(restrictedPaths)
- .append(", sizeDivisorMultiplicator=").append(sizeDivisorMultiplicator)
- .append(", fileSizeDisplayScale=").append(fileSizeDisplayScale).append(", zipUseTempFile=")
- .append(zipUseTempFile).append(", zipCompessionLevel=").append(zipCompessionLevel)
- .append(", zipTempDir=").append(zipTempDir).append(", downloadAllowed=").append(downloadAllowed)
- .append(", downloadCompressedAllowed=").append(downloadCompressedAllowed).append(", securityRoles=")
- .append(securityRoles).append(", componentPosition=").append(componentPosition)
- .append(", uploadAllowed=").append(uploadAllowed).append(", createFolderAllowed=")
- .append(createFolderAllowed).append(", delteFolderAllowed=").append(delteFolderAllowed)
- .append(", delteFileAllowed=").append(delteFileAllowed).append(", infoCrc32=").append(infoCrc32)
- .append(", infoMD5=").append(infoMD5).append(", infoSha1=").append(infoSha1).append(", infoSha256=")
- .append(infoSha256).append(", maxFilesizeForHashes=").append(maxFilesizeForHashes)
- .append(", countFolderSize=").append(countFolderSize).append("]");
- return builder.toString();
- }
-}
+package de.chandre.admintool.filebrowser;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import de.chandre.admintool.core.AdminToolConfig;
+
+/**
+ * configuration class for file uploader
+ *
+ * @author André
+ * @since 1.0.0
+ */
+@Component("adminToolFilebrowserConfig")
+public class AdminToolFilebrowserConfig implements AdminToolConfig {
+
+ private static final Log LOGGER = LogFactory.getLog(AdminToolFilebrowserConfig.class);
+
+ @Value("${admintool.filebrowser.enabled:true}")
+ private boolean enabled;
+
+ @Value("${admintool.filebrowser.hideMenuItem:false}")
+ private boolean hideMenuItem;
+
+ @Value("${admintool.filebrowser.startDir:}")
+ private String startDir;
+
+ @Value("#{'${admintool.filebrowser.forbiddenDrives:}'.split(';')}")
+ private List forbiddenDrives= new ArrayList<>();
+
+ @Value("${admintool.filebrowser.readOnly:false}")
+ private boolean readOnly;
+
+ @Value("${admintool.filebrowser.restrictedBrowsing:false}")
+ private boolean restrictedBrowsing;
+
+ @Value("${admintool.filebrowser.restrictedBrowsingIsWhitelist:true}")
+ private boolean restrictedBrowsingIsWhitelist;
+
+ @Value("#{'${admintool.filebrowser.restrictedPaths:}'.split(';')}")
+ private List restrictedPaths = new ArrayList<>();
+
+ @Value("${admintool.filebrowser.sizeDivisorMultiplicator:1000}")
+ private long sizeDivisorMultiplicator;
+
+ @Value("${admintool.filebrowser.fileSizeDisplayScale:2}")
+ private byte fileSizeDisplayScale;
+
+ @Value("${admintool.filebrowser.zipUseTempFile:true}")
+ private boolean zipUseTempFile;
+
+ @Value("${admintool.filebrowser.zipCompessionLevel:1}")
+ private byte zipCompessionLevel;
+
+ @Value("${admintool.filebrowser.zipTempDir:'sys:java.io.tmpdir'}")
+ private String zipTempDir;
+
+ @Value("${admintool.filebrowser.downloadAllowed:true}")
+ private boolean downloadAllowed;
+
+ @Value("${admintool.filebrowser.downloadCompressedAllowed:true}")
+ private boolean downloadCompressedAllowed;
+
+ @Value("#{'${admintool.filebrowser.securityRoles:}'.split(';')}")
+ private Set securityRoles = new HashSet<>();
+
+ @Value("${admintool.filebrowser.componentPosition:}")
+ private Integer componentPosition;
+
+ @Value("${admintool.filebrowser.uploadAllowed:false}")
+ private boolean uploadAllowed;
+
+ @Value("${admintool.filebrowser.createFolderAllowed:false}")
+ private boolean createFolderAllowed;
+
+ @Value("${admintool.filebrowser.delteFolderAllowed:false}")
+ private boolean delteFolderAllowed;
+
+ @Value("${admintool.filebrowser.delteFileAllowed:false}")
+ private boolean delteFileAllowed;
+
+ @Value("${admintool.filebrowser.notDeletableIfNotWriteable:true}")
+ private boolean notDeletableIfNotWriteable;
+
+ @Value("${admintool.filebrowser.info.crc32:true}")
+ private boolean infoCrc32;
+
+ @Value("${admintool.filebrowser.info.md5:true}")
+ private boolean infoMD5;
+
+ @Value("${admintool.filebrowser.info.sha1:true}")
+ private boolean infoSha1;
+
+ @Value("${admintool.filebrowser.info.sha256:false}")
+ private boolean infoSha256;
+
+ @Value("${admintool.filebrowser.info.maxFilesizeForHashes:1000000000}")
+ private long maxFilesizeForHashes;
+
+ @Value("${admintool.filebrowser.info.countFolderSize:true}")
+ private boolean countFolderSize;
+
+ @Override
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ /**
+ * @return the hideMenuItem
+ */
+ public boolean isHideMenuItem() {
+ return hideMenuItem;
+ }
+
+ /**
+ * @return the startDir
+ */
+ public File getStartDir() {
+ if (StringUtils.isEmpty(this.startDir)) {
+ return new File(this.getClass().getClassLoader().getResource("").getPath());
+ }
+ File start = new File(this.startDir);
+ return start.isFile() ? start.getParentFile() : start;
+ }
+
+ /**
+ * @return the forbiddenDrives
+ */
+ public List getForbiddenDrives() {
+ return forbiddenDrives;
+ }
+
+ /**
+ * @return the readOnly
+ */
+ public boolean isReadOnly() {
+ return isEnabled() && readOnly;
+ }
+
+ /**
+ * @return the restrictBrowsing
+ */
+ public boolean isRestrictedBrowsing() {
+ return restrictedBrowsing;
+ }
+
+ /**
+ * @return the restrictedBrowsingIsWhitelist
+ */
+ public boolean isRestrictedBrowsingIsWhitelist() {
+ return restrictedBrowsingIsWhitelist;
+ }
+
+ /**
+ * @return the restrictedPaths
+ */
+ public List getRestrictedPaths() {
+ return restrictedPaths;
+ }
+
+ /**
+ * @return the sizeDivisorMultiplicator
+ */
+ public long getSizeDivisorMultiplicator() {
+ return sizeDivisorMultiplicator;
+ }
+
+ /**
+ * @return the sizeDivisorMultiplicator
+ */
+ public void setSizeDivisorMultiplicator(long sizeDivisorMultiplicator) {
+ this.sizeDivisorMultiplicator = sizeDivisorMultiplicator;
+ }
+
+ /**
+ * @return the fileSizeDisplayScale
+ */
+ public byte getFileSizeDisplayScale() {
+ return fileSizeDisplayScale;
+ }
+
+ public void setFileSizeDisplayScale(byte fileSizeDisplayScale) {
+ this.fileSizeDisplayScale = fileSizeDisplayScale;
+ }
+
+ /**
+ * @return the zipUseTempFile
+ */
+ public boolean isZipUseTempFile() {
+ return zipUseTempFile;
+ }
+
+ /**
+ * @return the zipTempDir
+ */
+ public String getZipTempDir() {
+ if (this.zipTempDir.charAt(0) == '\''){
+ return zipTempDir.substring(1).substring(0, this.zipTempDir.length() -2);
+ }
+ return zipTempDir;
+ }
+
+ /**
+ * @return the zipCompessionLevel
+ */
+ public byte getZipCompessionLevel() {
+ return zipCompessionLevel;
+ }
+
+ /**
+ * @return the downloadAllowed
+ */
+ public boolean isDownloadAllowed() {
+ return isEnabled() && downloadAllowed;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.0.6
+ */
+ public boolean isDownloadCompressedAllowed() {
+ return isEnabled() && downloadCompressedAllowed;
+ }
+
+ /**
+ * @return the securityRoles
+ * @since 1.0.1
+ */
+ public Set getSecurityRoles() {
+ return securityRoles;
+ }
+
+ /**
+ * @return the componentPosition
+ * @since 1.0.1
+ */
+ public Integer getComponentPosition() {
+ return componentPosition;
+ }
+
+ /**
+ * @param componentPosition the componentPosition to set
+ * @since 1.0.1
+ */
+ public void setComponentPosition(Integer componentPosition) {
+ this.componentPosition = componentPosition;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isManipulationAllowed() {
+ return !readOnly && (uploadAllowed || createFolderAllowed);
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isUploadAllowed() {
+ return isEnabled() && uploadAllowed;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isCreateFolderAllowed() {
+ return isEnabled() && createFolderAllowed;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isDelteFolderAllowed() {
+ return isEnabled() && delteFolderAllowed;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isDelteFileAllowed() {
+ return isEnabled() && delteFileAllowed;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6.2
+ */
+ public boolean isNotDeletableIfNotWriteable() {
+ return notDeletableIfNotWriteable;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isInfoCrc32() {
+ return infoCrc32;
+ }
+
+ public void setInfoCrc32(boolean infoCrc32) {
+ this.infoCrc32 = infoCrc32;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isInfoMD5() {
+ return infoMD5;
+ }
+
+ public void setInfoMD5(boolean infoMD5) {
+ this.infoMD5 = infoMD5;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isInfoSha1() {
+ return infoSha1;
+ }
+
+ public void setInfoSha1(boolean infoSha1) {
+ this.infoSha1 = infoSha1;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isInfoSha256() {
+ return infoSha256;
+ }
+
+ public void setInfoSha256(boolean infoSha256) {
+ this.infoSha256 = infoSha256;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.0.6
+ */
+ public long getMaxFilesizeForHashes() {
+ return maxFilesizeForHashes;
+ }
+
+ public void setMaxFilesizeForHashes(long maxFilesizeForHashes) {
+ this.maxFilesizeForHashes = maxFilesizeForHashes;
+ }
+
+ /**
+ *
+ * @return
+ * @since 1.1.6
+ */
+ public boolean isCountFolderSize() {
+ return countFolderSize;
+ }
+
+ public void setCountFolderSize(boolean countFolderSize) {
+ this.countFolderSize = countFolderSize;
+ }
+
+ @Override
+ @PostConstruct
+ public void printConfig() {
+ LOGGER.debug(toString());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("AdminToolFilebrowserConfig [enabled=").append(enabled).append(", hideMenuItem=")
+ .append(hideMenuItem).append(", startDir=").append(startDir).append(", forbiddenDrives=")
+ .append(forbiddenDrives).append(", readOnly=").append(readOnly).append(", restrictedBrowsing=")
+ .append(restrictedBrowsing).append(", restrictedBrowsingIsWhitelist=")
+ .append(restrictedBrowsingIsWhitelist).append(", restrictedPaths=").append(restrictedPaths)
+ .append(", sizeDivisorMultiplicator=").append(sizeDivisorMultiplicator)
+ .append(", fileSizeDisplayScale=").append(fileSizeDisplayScale).append(", zipUseTempFile=")
+ .append(zipUseTempFile).append(", zipCompessionLevel=").append(zipCompessionLevel)
+ .append(", zipTempDir=").append(zipTempDir).append(", downloadAllowed=").append(downloadAllowed)
+ .append(", downloadCompressedAllowed=").append(downloadCompressedAllowed).append(", securityRoles=")
+ .append(securityRoles).append(", componentPosition=").append(componentPosition)
+ .append(", uploadAllowed=").append(uploadAllowed).append(", createFolderAllowed=")
+ .append(createFolderAllowed).append(", delteFolderAllowed=").append(delteFolderAllowed)
+ .append(", delteFileAllowed=").append(delteFileAllowed).append(", infoCrc32=").append(infoCrc32)
+ .append(", infoMD5=").append(infoMD5).append(", infoSha1=").append(infoSha1).append(", infoSha256=")
+ .append(infoSha256).append(", maxFilesizeForHashes=").append(maxFilesizeForHashes)
+ .append(", countFolderSize=").append(countFolderSize).append("]");
+ return builder.toString();
+ }
+}
diff --git a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserController.java b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserController.java
index 7495b69..e6b05c5 100644
--- a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserController.java
+++ b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserController.java
@@ -1,230 +1,254 @@
-package de.chandre.admintool.filebrowser;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.ModelAndView;
-
-import de.chandre.admintool.core.AdminTool;
-import de.chandre.admintool.core.controller.AbstractAdminController;
-
-/**
- * Filebrowser controller
- * requires admintool-core 1.0.1
- * @author Andre
- * @since 1.0.1
- */
-@Controller
-@RequestMapping(AdminTool.ROOTCONTEXT + "/filebrowser")
-public class AdminToolFilebrowserController extends AbstractAdminController {
-
- private static final Log LOGGER = LogFactory.getLog(AdminToolFilebrowserController.class);
-
- @Autowired
- private AdminToolFilebrowserService filebrowserService;
-
- @Autowired
- private AdminToolFilebrowserConfig filebrowserConfig;
-
- @RequestMapping(value = {"", "/","/dir"}, method={RequestMethod.GET, RequestMethod.POST})
- public String showDirectory(@RequestParam(name = "dir", required = false) String dirPath,
- @RequestParam(name = "sortCol", required = false) String sortCol,
- @RequestParam(name = "sortAsc", required = false, defaultValue = "true") boolean sortType,
- @RequestParam(name = "filter", required = false) String filter,
- ModelMap model, HttpServletRequest request) throws UnsupportedEncodingException {
- if (!filebrowserConfig.isEnabled()) {
- return null;
- }
- String currentDir = StringUtils.isEmpty(dirPath) ? filebrowserConfig.getStartDir().getAbsolutePath() : URLDecoder.decode(dirPath, "UTF-8");
- if(LOGGER.isTraceEnabled()) LOGGER.trace("show directory: " + currentDir);
- String templatePath = addCommonContextVars(model, request, "filebrowser", null);
- model.put("currentDir", currentDir);
- model.put("sortCol", SortColumn.fromIndex(sortCol));
- model.put("sortAsc", sortType);
- model.put("filter", filter);
-
- return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
- }
-
- @RequestMapping(value = {"/info"}, method={RequestMethod.GET, RequestMethod.POST})
- public String info(@RequestParam("file") String filePath, ModelMap model, HttpServletRequest request,
- HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return null;
- }
- String decodedPath = URLDecoder.decode(filePath, "UTF-8");
- if(LOGGER.isTraceEnabled()) LOGGER.trace("info: " + decodedPath);
- addCommonContextVars(model, request);
- model.addAttribute("infos", filebrowserService.getFileInfo(decodedPath));
- return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + "filebrowser/includes/fileInfo.inc";
- }
-
- @RequestMapping(value = {"/file"}, method={RequestMethod.GET, RequestMethod.POST})
- public void showFile(@RequestParam("file") String filePath, ModelMap model, HttpServletRequest request,
- HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return;
- }
- if (!filebrowserConfig.isDownloadAllowed()) {
- throw new DownloadNotAllowedException("file download is deactivated by configuration");
- }
- String decodedPath = URLDecoder.decode(filePath, "UTF-8");
- if(LOGGER.isTraceEnabled()) LOGGER.trace("download file: " + decodedPath);
- filebrowserService.downloadFile(decodedPath, response, false);
- }
-
- @RequestMapping(value = {"/download"}, method={RequestMethod.GET, RequestMethod.POST})
- public void download(@RequestParam("file") String filePath, ModelMap model, HttpServletRequest request,
- HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return;
- }
- if (!filebrowserConfig.isDownloadAllowed()) {
- throw new DownloadNotAllowedException("file download is deactivated by configuration");
- }
- String decodedPath = URLDecoder.decode(filePath, "UTF-8");
- if(LOGGER.isTraceEnabled()) LOGGER.trace("download file: " + decodedPath);
- filebrowserService.downloadFile(decodedPath, response, true);
- }
-
- @RequestMapping(value = {"/zip"}, method={RequestMethod.GET, RequestMethod.POST})
- public void downloadAsZip(@RequestParam("selectedFile") List filePaths, ModelMap model, HttpServletRequest request,
- HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return;
- }
- if (!filebrowserConfig.isDownloadCompressedAllowed()) {
- throw new DownloadNotAllowedException("compressed file download is deactivated by configuration");
- }
- List decodedPaths = new ArrayList<>();
- if (null != filePaths) {
- filePaths.forEach(filePath ->{
- try {
- decodedPaths.add(URLDecoder.decode(filePath, "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- LOGGER.error(e.getMessage(), e);
- }
- });
- }
-
- if(LOGGER.isTraceEnabled()) LOGGER.trace("downloadAsZip file: " + decodedPaths.size());
- filebrowserService.downloadFilesAsZip(decodedPaths, response);
- }
-
- @RequestMapping(value = {"/upload"}, method={RequestMethod.POST})
- @ResponseBody
- public FileUploadResponse upload(@RequestParam("qqfile") MultipartFile qqfile,
- @RequestParam("qqfilename") String qqfilename,
- @RequestParam("qquuid") String qquuid,
- @RequestParam("currentDir") String currentDir,
- HttpServletRequest request, HttpServletResponse response)
- throws IOException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return null;
- }
- if(LOGGER.isTraceEnabled()) LOGGER.trace("upload file: " + qqfilename);
- FileUploadResponse fur = new FileUploadResponse();
- if (!filebrowserConfig.isUploadAllowed()) {
- fur.setError("file upload is not allowed");
- return fur;
- }
- String decodedPath = URLDecoder.decode(currentDir, "UTF-8");
-
- try {
- fur.setSuccess(filebrowserService.saveFile(decodedPath, qqfile));
- if (!fur.isSuccess()) {
- fur.setError("unable to opload file");
- }
- } catch (Exception e) {
- fur.setError(e.getMessage());
- }
-
- return fur;
- }
-
- @RequestMapping(value = {"/createFolder"}, method={RequestMethod.POST})
- public void createFolder(@RequestParam("folderName") String folderPath, @RequestParam("currentDir") String currentDir,
- ModelMap model, HttpServletRequest request, HttpServletResponse response)
- throws IOException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return;
- }
- if (!filebrowserConfig.isCreateFolderAllowed()) {
- throw new GenericFilebrowserException("folder creation not allowed");
- }
- String decodedPath = URLDecoder.decode(currentDir, "UTF-8");
- String decodedFolder = URLDecoder.decode(folderPath, "UTF-8");
- if(LOGGER.isTraceEnabled()) LOGGER.trace("create folder: " + decodedFolder + " in path: " + decodedPath);
- String path = filebrowserService.createFolder(decodedPath, decodedFolder);
- if (null != path) {
- path = URLEncoder.encode(path, "UTF-8");
- } else {
- path = folderPath;
- }
- response.sendRedirect(AdminTool.ROOTCONTEXT + "/filebrowser?dir=" + path);
- }
-
- @RequestMapping(value = {"/delete"}, method={RequestMethod.POST})
- public void deleteResource(
- @RequestParam(name ="file", required=false) String filePath,
- ModelMap model, HttpServletRequest request, HttpServletResponse response)
- throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
- if (!filebrowserConfig.isEnabled()) {
- return;
- }
- String decodedPath = URLDecoder.decode(filePath, "UTF-8");
- if(LOGGER.isTraceEnabled()) LOGGER.trace("delete resource: " + decodedPath);
-
- String path = filebrowserService.deleteResource(decodedPath);
- if (null != path) {
- path = URLEncoder.encode(path, "UTF-8");
- } else {
- path = filePath;
- }
- response.sendRedirect(AdminTool.ROOTCONTEXT + "/filebrowser?dir=" + path);
- }
-
- @ExceptionHandler({DownloadNotAllowedException.class, GenericFilebrowserException.class})
- public ModelAndView handleException(Exception exception, HttpServletRequest request) throws IOException {
- if(LOGGER.isTraceEnabled()) LOGGER.trace("handleException: " + exception.getMessage());
-
- ModelAndView mv = new ModelAndView(AdminTool.GENERIC_ERROR_TPL_PATH);
- addCommonContextVars(mv.getModelMap(), request, "filebrowser", null);
-
- String lastFile = request.getParameter("file");
- if (StringUtils.isEmpty(lastFile)) {
- lastFile = request.getParameter("selectedFile");
- }
- String decodedPath = URLDecoder.decode(lastFile, "UTF-8");
- LOGGER.info("lastFile: " + lastFile);
- if (StringUtils.hasLength(decodedPath) &&
- filebrowserService.isAllowed(new File(decodedPath).getParentFile(), false, filebrowserConfig.isReadOnly()) ) {
- mv.getModelMap().put("currentDir", new File(decodedPath).getParent());
- } else {
- mv.getModelMap().put("currentDir", filebrowserConfig.getStartDir().getAbsolutePath());
- }
- mv.getModelMap().put("exceptionMessage", exception.getMessage());
- return mv;
- }
-
-}
+package de.chandre.admintool.filebrowser;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+
+import de.chandre.admintool.core.AdminTool;
+import de.chandre.admintool.core.controller.AbstractAdminController;
+
+/**
+ * Filebrowser controller
+ * requires admintool-core 1.0.1
+ * @author Andre
+ * @since 1.0.1
+ */
+@Controller
+@RequestMapping(AdminTool.ROOTCONTEXT + "/filebrowser")
+public class AdminToolFilebrowserController extends AbstractAdminController {
+
+ private static final Log LOGGER = LogFactory.getLog(AdminToolFilebrowserController.class);
+
+ @Autowired
+ private AdminToolFilebrowserService filebrowserService;
+
+ @Autowired
+ private AdminToolFilebrowserConfig filebrowserConfig;
+
+ @RequestMapping(value = {"", "/","/dir"}, method={RequestMethod.GET, RequestMethod.POST})
+ public String showDirectory(@RequestParam(name = "dir", required = false) String dirPath,
+ @RequestParam(name = "sortCol", required = false) String sortCol,
+ @RequestParam(name = "sortAsc", required = false, defaultValue = "true") boolean sortType,
+ @RequestParam(name = "filter", required = false) String filter,
+ ModelMap model, HttpServletRequest request) throws UnsupportedEncodingException {
+ if (!filebrowserConfig.isEnabled()) {
+ return null;
+ }
+ String currentDir = StringUtils.isEmpty(dirPath) ? filebrowserConfig.getStartDir().getAbsolutePath() : URLDecoder.decode(dirPath, "UTF-8");
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("show directory: " + currentDir);
+ String templatePath = addCommonContextVars(model, request, "filebrowser", null);
+ model.put("currentDir", currentDir);
+ model.put("sortCol", SortColumn.fromIndex(sortCol));
+ model.put("sortAsc", sortType);
+ model.put("filter", filter);
+
+ return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
+ }
+
+ @RequestMapping(value = {"/info"}, method={RequestMethod.GET, RequestMethod.POST})
+ public String info(@RequestParam("file") String filePath, ModelMap model, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
+ if (!filebrowserConfig.isEnabled()) {
+ return null;
+ }
+ String decodedPath = URLDecoder.decode(filePath, "UTF-8");
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("info: " + decodedPath);
+ addCommonContextVars(model, request);
+ model.addAttribute("infos", filebrowserService.getFileInfo(decodedPath));
+ return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + "filebrowser/includes/fileInfo.inc";
+ }
+
+ @RequestMapping(value = {"/file"}, method={RequestMethod.GET, RequestMethod.POST})
+ public void showFile(@RequestParam("file") String filePath, ModelMap model, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
+ if (!filebrowserConfig.isEnabled()) {
+ return;
+ }
+ if (!filebrowserConfig.isDownloadAllowed()) {
+ throw new DownloadNotAllowedException("file download is deactivated by configuration");
+ }
+ String decodedPath = URLDecoder.decode(filePath, "UTF-8");
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("download file: " + decodedPath);
+ filebrowserService.downloadFile(decodedPath, response, false);
+ }
+
+ @RequestMapping(value = {"/download"}, method={RequestMethod.GET, RequestMethod.POST})
+ public void download(@RequestParam("file") String filePath, ModelMap model, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
+ if (!filebrowserConfig.isEnabled()) {
+ return;
+ }
+ if (!filebrowserConfig.isDownloadAllowed()) {
+ throw new DownloadNotAllowedException("file download is deactivated by configuration");
+ }
+ String decodedPath = URLDecoder.decode(filePath, "UTF-8");
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("download file: " + decodedPath);
+ filebrowserService.downloadFile(decodedPath, response, true);
+ }
+
+ @RequestMapping(value = {"/zip"}, method={RequestMethod.GET, RequestMethod.POST})
+ public void downloadAsZip(@RequestParam("selectedFile") List filePaths, ModelMap model, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, DownloadNotAllowedException, GenericFilebrowserException {
+ if (!filebrowserConfig.isEnabled()) {
+ return;
+ }
+ if (!filebrowserConfig.isDownloadCompressedAllowed()) {
+ throw new DownloadNotAllowedException("compressed file download is deactivated by configuration");
+ }
+ List decodedPaths = new ArrayList<>();
+ if (null != filePaths) {
+ filePaths.forEach(filePath ->{
+ try {
+ decodedPaths.add(URLDecoder.decode(filePath, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ });
+ }
+
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("downloadAsZip file: " + decodedPaths.size());
+ filebrowserService.downloadFilesAsZip(decodedPaths, response);
+ }
+
+ @RequestMapping(value = {"/upload"}, method={RequestMethod.POST})
+ @ResponseBody
+ public FileUploadResponse upload(@RequestParam("qqfile") MultipartFile qqfile,
+ @RequestParam("qqfilename") String qqfilename,
+ @RequestParam("qquuid") String qquuid,
+ @RequestParam("currentDir") String currentDir,
+ HttpServletRequest request, HttpServletResponse response)
+ throws IOException, GenericFilebrowserException {
+ if (!filebrowserConfig.isEnabled()) {
+ return null;
+ }
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("upload file: " + qqfilename);
+ FileUploadResponse fur = new FileUploadResponse();
+ if (!filebrowserConfig.isUploadAllowed()) {
+ fur.setError("file upload is not allowed");
+ return fur;
+ }
+ String decodedPath = URLDecoder.decode(currentDir, "UTF-8");
+
+ try {
+ fur.setSuccess(filebrowserService.saveFile(decodedPath, qqfile));
+ if (!fur.isSuccess()) {
+ fur.setError("unable to opload file");
+ }
+ } catch (Exception e) {
+ fur.setError(e.getMessage());
+ }
+
+ return fur;
+ }
+
+ @RequestMapping(value = {"/createFolder"}, method={RequestMethod.POST})
+ public String createFolder(@RequestParam("folderName") String folderPath, @RequestParam("currentDir") String currentDir,
+ ModelMap model, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, GenericFilebrowserException {
+ if (!filebrowserConfig.isEnabled()) {
+ return null;
+ }
+ if (!filebrowserConfig.isCreateFolderAllowed()) {
+ throw new GenericFilebrowserException("folder creation not allowed");
+ }
+ String decodedPath = URLDecoder.decode(currentDir, "UTF-8");
+ String decodedFolder = URLDecoder.decode(folderPath, "UTF-8");
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("create folder: " + decodedFolder + " in path: " + decodedPath);
+
+ String path = null;
+ try {
+ path = filebrowserService.createFolder(decodedPath, decodedFolder);
+ } catch (GenericFilebrowserException e) {
+ String templatePath = addCommonContextVars(model, request, "filebrowser", null);
+ model.put("exceptionMessage", e.getMessage());
+ model.put("currentDir", decodedPath);
+ return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
+ }
+ if (null != path) {
+ path = URLEncoder.encode(path, "UTF-8");
+ } else {
+ path = folderPath;
+ }
+ response.sendRedirect(getRootContext(request)+ "/filebrowser?dir=" + path);
+ return null;
+ }
+
+ @RequestMapping(value = {"/delete"}, method={RequestMethod.POST})
+ public String deleteResource(
+ @RequestParam(name ="file", required=false) String filePath,
+ ModelMap model, HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
+ if (!filebrowserConfig.isEnabled()) {
+ return null;
+ }
+
+ String decodedPath = URLDecoder.decode(filePath, "UTF-8");
+ LOGGER.info("delete resource: " + decodedPath);
+
+ String path = null;
+ try {
+ path = filebrowserService.deleteResource(decodedPath);
+ } catch (GenericFilebrowserException e) {
+ String templatePath = addCommonContextVars(model, request, "filebrowser", null);
+ model.put("exceptionMessage", e.getMessage());
+ File currentPath = StringUtils.isEmpty(decodedPath) ? filebrowserConfig.getStartDir() : new File(decodedPath);
+ model.put("currentDir", currentPath.isDirectory() ? currentPath : currentPath.getParent());
+ return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
+ }
+ if (null != path) {
+ path = URLEncoder.encode(path, "UTF-8");
+ } else {
+ path = filePath;
+ }
+ response.sendRedirect(getRootContext(request) + "/filebrowser?dir=" + path);
+ return null;
+ }
+
+ @ExceptionHandler({DownloadNotAllowedException.class, GenericFilebrowserException.class})
+ public ModelAndView handleException(Exception exception, HttpServletRequest request) throws IOException {
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("handleException: " + exception.getMessage());
+
+ ModelAndView mv = new ModelAndView(AdminTool.GENERIC_ERROR_TPL_PATH);
+ addCommonContextVars(mv.getModelMap(), request, "filebrowser", null);
+
+ String lastFile = request.getParameter("file");
+ if (StringUtils.isEmpty(lastFile)) {
+ lastFile = request.getParameter("selectedFile");
+ }
+ String decodedPath = null;
+ if (StringUtils.hasLength(lastFile)) {
+ decodedPath = URLDecoder.decode(lastFile, "UTF-8");
+ }
+ LOGGER.info("lastFile: " + lastFile);
+ if (StringUtils.hasLength(decodedPath) &&
+ filebrowserService.isAllowed(new File(decodedPath).getParentFile(), false, filebrowserConfig.isReadOnly()) ) {
+ mv.getModelMap().put("currentDir", new File(decodedPath).getParent());
+ } else {
+ mv.getModelMap().put("currentDir", filebrowserConfig.getStartDir().getAbsolutePath());
+ }
+ mv.getModelMap().put("exceptionMessage", exception.getMessage());
+ return mv;
+ }
+
+}
diff --git a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserServiceImpl.java b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserServiceImpl.java
index e7a0553..af37451 100644
--- a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserServiceImpl.java
+++ b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/filebrowser/AdminToolFilebrowserServiceImpl.java
@@ -1,681 +1,691 @@
-package de.chandre.admintool.filebrowser;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.DosFileAttributes;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-import org.springframework.web.multipart.MultipartFile;
-
-import de.chandre.admintool.core.utils.RegexUtil;
-
-/**
- *
- * @author Andre
- * @since 1.0.1
- */
-@Service("adminToolFilebrowserService")
-public class AdminToolFilebrowserServiceImpl extends AbstractFileBrowserService implements AdminToolFilebrowserService {
-
- private static final Log LOGGER = LogFactory.getLog(AdminToolFilebrowserServiceImpl.class);
-
- private static final Map FILE_SIZE_EXP = new TreeMap<>();
- static {
- FILE_SIZE_EXP.put(Integer.valueOf(1), " B");
- FILE_SIZE_EXP.put(Integer.valueOf(2), " KB");
- FILE_SIZE_EXP.put(Integer.valueOf(3), " MB");
- FILE_SIZE_EXP.put(Integer.valueOf(4), " GB");
- FILE_SIZE_EXP.put(Integer.valueOf(5), " TB");
- FILE_SIZE_EXP.put(Integer.valueOf(6), " PB");
- FILE_SIZE_EXP.put(Integer.valueOf(7), " EB");
- }
-
- @Autowired
- private AdminToolFilebrowserConfig config;
-
- @Autowired
- private Environment env;
-
- private Set rootDirsCache = Collections.newSetFromMap(new ConcurrentHashMap<>());
-
-
- @Override
- public Set getRootDirs() {
- File[] roots = File.listRoots();
-
- if (this.rootDirsCache.isEmpty()) {
- for (File file : roots) {
- if(!config.getForbiddenDrives().contains(file.getAbsolutePath().toLowerCase())) {
- // if not forbidden add it to result
- this.rootDirsCache.add(file.getAbsolutePath());
- }
- }
- }
- return this.rootDirsCache;
- }
-
- @Override
- public boolean isRootActive(String rootDir, String currentDir) {
- if (!StringUtils.isEmpty(currentDir) && currentDir.toLowerCase().startsWith(rootDir.toLowerCase())) {
- return true;
- }
- return false;
- }
-
- @Override
- public String getParent(String dir) throws IOException {
- File file = new File(dir);
- if (null != file.getParent() && isAllowed(file.getParentFile(), false)) {
- return file.getParent();
- }
- return "";
- }
-
- protected List sort(File[] fileAr, final SortColumn sortCol, Boolean sortAsc) {
- List files = Arrays.asList(fileAr);
- if (null == sortCol) {
- return files;
- }
- if (null == sortAsc) {
- sortAsc = Boolean.TRUE;
- }
- final int direction = sortAsc.booleanValue() ? 1 : -1;
-
- Collections.sort(files, new Comparator() {
- @Override
- public int compare(File o1, File o2) {
- try {
- switch (sortCol) {
- case DATE:
- return getLastChange(o1).compareTo(getLastChange(o2)) * direction;
- case SIZE:
- return Long.valueOf(o1.length()).compareTo(Long.valueOf(o2.length())) * direction;
- case TYPE:
- return getFileType(o1).compareTo(getFileType(o2)) * direction;
- case NAME:
- default:
- return o1.getName().compareTo(o2.getName()) * direction;
- }
- } catch (Exception ignore) {
- }
-
- return 0;
- }
- });
- return files;
- }
-
- @Override
- public String getSortDirection(int current, SortColumn sortCol, Boolean sortAsc) {
- if (current == sortCol.getIndex() && sortAsc != null) {
- return sortAsc ? "up" : "down";
- }
- return "";
- }
-
- @Override
- public List getDirectories(String currentDir, SortColumn sortCol, Boolean sortAsc, String filter) throws IOException {
- File file = new File(currentDir);
- if (null != file && isAllowed(file, false)) {
- final Pattern fileNamePattern = getFileNamePattern(filter);
- File[] files = file.listFiles(new FileFilter() {
- @Override
- public boolean accept(File dir) {
- try {
- if (isAllowed(dir, false) && dir.isDirectory()) {
- if (null == fileNamePattern) {
- return true;
- } else if (null != fileNamePattern && fileNamePattern.matcher(dir.getName()).matches()) {
- return true;
- }
- }
- } catch (IOException e) {
- LOGGER.debug(e.getMessage(), e);
- }
- return false;
- }
- });
- if (files != null) {
- return sort(files, sortCol, sortAsc);
- }
- }
- return Collections.emptyList();
- }
-
- @Override
- public List getFiles(String currentDir, SortColumn sortCol, Boolean sortAsc, String filter) throws IOException {
- File file = new File(currentDir);
- if (null != file && isAllowed(file, false)) {
- final Pattern fileNamePattern = getFileNamePattern(filter);
- File[] files = file.listFiles(new FileFilter() {
- @Override
- public boolean accept(File dir) {
- try {
- if (isAllowed(dir, false) && dir.isFile()) {
- if (null == fileNamePattern) {
- return true;
- } else if (null != fileNamePattern && fileNamePattern.matcher(dir.getName()).matches()) {
- return true;
- }
- }
- } catch (IOException e) {
- LOGGER.debug(e.getMessage(), e);
- }
- return false;
- }
- });
- if (files != null) {
- return sort(files, sortCol, sortAsc);
- }
- }
- return Collections.emptyList();
- }
-
- private Pattern getFileNamePattern(String filter) {
- if (!StringUtils.isEmpty(filter)) {
- return Pattern.compile(RegexUtil.wildcardToRegex(filter), Pattern.CASE_INSENSITIVE);
- }
- return null;
- }
-
- @Override
- public String getDirOrRootName(File currentDir) {
- if (StringUtils.isEmpty(currentDir.getName())) {
- return currentDir.getAbsolutePath();
- }
- return currentDir.getName();
- }
-
- @Override
- public List getBreadcrumb(String currentDir) {
- if (null == currentDir) {
- return Collections.emptyList();
- }
- List result = new ArrayList<>();
- File file = new File(currentDir);
- getParentsRecursive(file, result);
- return result;
- }
-
- private void getParentsRecursive(File actual, List files) {
- if (null != actual.getParentFile()) {
- getParentsRecursive(actual.getParentFile(), files);
- }
- if (actual.isDirectory()) {
- files.add(actual);
- }
- }
-
-
-
- @Override
- public String getFileSizeSum(String dir, String filter) throws IOException {
- List files = getFiles(dir, null, true, filter);
- Long res = files.stream().collect(Collectors.summingLong(File::length));
- return String.format("%s in %s files", getFileSize(res), files.size());
- }
-
- @Override
- public Date getLastChange(File file) throws IOException {
- if (null != file) {
- FileTime time = Files.getLastModifiedTime(file.toPath(), new LinkOption[]{});
- return new Date(time.toMillis());
- }
- return new Date();
- }
-
- @Override
- public String getFileType(File file) {
- if (null == file) {
- return "";
- }
- if (file.isDirectory()) {
- return "DIR";
- }
- if (file.getName().lastIndexOf('.') == -1) {
- return "";
- }
- return getExtension(file);
- }
-
- @Override
- public String getFileSize(File file) {
- return getFileSize(file.length());
- }
-
- /**
- * returns the the size in B, KB, MB or GB depending on the length
- *
- * @param fileLength
- * @return
- */
- @Override
- public String getFileSize(long fileLength) {
- return calculateDisplayFileSize(fileLength, config.getSizeDivisorMultiplicator());
- }
-
- @Override
- public String getNormalFileSize(long fileLength) {
- return calculateDisplayFileSize(fileLength, FileUtils.ONE_KB);
- }
-
- protected String calculateDisplayFileSize(long fileLength, long sizeDivisorMultiplicator) {
- BigInteger multiplicator = BigInteger.valueOf(sizeDivisorMultiplicator);
- BigInteger size = BigInteger.valueOf(fileLength);
-
- for (Entry entry : FILE_SIZE_EXP.entrySet()) {
- BigInteger divisor = multiplicator.pow(entry.getKey().intValue());
- if (fileLength < divisor.longValue()) {
- if (entry.getKey().intValue() == 1) {
- return String.valueOf(size) + entry.getValue();
- }
- return formatFileSize(size, multiplicator.pow(entry.getKey().intValue() - 1), entry.getValue());
- }
- }
- //should not happen
- return FileUtils.byteCountToDisplaySize(fileLength);
- }
-
- /**
- * calculates the and formats files size
- * @see #getFileSize(long)
- * @param fileLength
- * @param divisor
- * @param unit the Unit for the divisor
- * @return
- */
- protected String formatFileSize(BigInteger fileLength, BigInteger divisor, String unit) {
- BigDecimal size = new BigDecimal(fileLength);
- size = size.setScale(config.getFileSizeDisplayScale()).divide(new BigDecimal(divisor), BigDecimal.ROUND_HALF_EVEN);
- return String.format("%s %s", size.doubleValue(), unit);
- }
-
- /**
- *
- * @param fileName the name
- * @param size (optional) size/length of content
- * @param response the servlet response
- */
- protected void prepareDownloadResponse(String fileName, Long size, HttpServletResponse response, boolean asAttachment) {
-
- String mimeType = MimeTypes.getMimeType(getExtension(fileName));
- LOGGER.info("following mimeType:" + mimeType);
- if (asAttachment || null == mimeType) {
- response.setContentType("application/octet-stream");
- response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
- } else {
- response.setContentType(mimeType);
- response.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
- }
- if (null != size) {
- response.setContentLength(size.intValue());
- }
- }
-
- @Override
- public void downloadFile(String filePath, HttpServletResponse response, boolean asAttachment) throws DownloadNotAllowedException, GenericFilebrowserException {
- downloadFile(filePath, response, null, asAttachment);
- }
-
- @Override
- public void downloadFile(String filePath, HttpServletResponse response, String alternativeFileName, boolean asAttachment) throws DownloadNotAllowedException, GenericFilebrowserException {
- File file = new File(filePath);
- try {
- if (!isAllowed(file, false)) {
- throw new DownloadNotAllowedException();
- }
- } catch (IOException e) {
- throw new GenericFilebrowserException(e);
- }
-
- prepareDownloadResponse(StringUtils.isEmpty(alternativeFileName) ? file.getName() : alternativeFileName,
- Long.valueOf(file.length()), response, asAttachment);
-
- InputStream in = null;
- BufferedInputStream fileInput = null;
- try {
- in = new FileInputStream(file);
- fileInput = new BufferedInputStream(in);
- ServletOutputStream out = response.getOutputStream();
- IOUtils.copy(fileInput, out, 8192);
- out.flush();
- } catch (IOException e) {
- throw new GenericFilebrowserException("could not prepare file for downloading", e);
- }
- finally {
- IOUtils.closeQuietly(fileInput);
- IOUtils.closeQuietly(in);
- }
- }
-
- @Override
- public void downloadFilesAsZip(List filePaths, HttpServletResponse response) throws GenericFilebrowserException {
-
- File tempFile = null;
- try {
- OutputStream out = null;
-
- try {
- if (config.isZipUseTempFile()) {
- String tempDirConf = config.getZipTempDir();
- File tempDir = null;
- if (StringUtils.isEmpty(tempDirConf)) {
- tempFile = File.createTempFile("zip", null);
- }
- else if (tempDirConf.startsWith("sys")) {
- tempDir = new File(System.getProperty(tempDirConf.substring(4, tempDirConf.length())));
- }
- else if (tempDirConf.startsWith("env")) {
- tempDir = new File(env.getProperty(tempDirConf.substring(4, tempDirConf.length())));
- }
- else {
- tempDir = new File(tempDirConf);
- }
- if (null == tempFile) {
- tempFile = File.createTempFile("zip", null, tempDir);
- }
- out = new FileOutputStream(tempFile);
- }
- } catch (Exception e) {
- LOGGER.warn("could not create temporary file, using servlet outputstream for serving ZIP");
- }
-
- if (null == out) {
- tempFile = null;
- prepareDownloadResponse("rename_me.zip", null, response, true);
- out = response.getOutputStream();
- }
-
- ZipOutputStream zos = new ZipOutputStream(out);
-
- zos.setLevel(config.getZipCompessionLevel());
- try {
- for (String filePathStr : filePaths) {
- File orgfile = new File(filePathStr);
- Files.walkFileTree(orgfile.toPath(), new SimpleFileVisitor() {
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- if (!isAllowed(file.toFile(), false)) {
- LOGGER.debug("ZIP creation: skipping not allowed file: " + file.toAbsolutePath());
- return FileVisitResult.CONTINUE;
- }
- String entry = orgfile.getParentFile().toPath().relativize(file).toString();
- LOGGER.trace("creating entry: " + entry);
- zos.putNextEntry(new ZipEntry(entry));
- Files.copy(file, zos);
- zos.closeEntry();
- return FileVisitResult.CONTINUE;
- }
-
- public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
- if (!isAllowed(dir.toFile(), false)) {
- LOGGER.debug("ZIP creation: skipping not allowed directory and subtree for: " + dir.toAbsolutePath());
- return FileVisitResult.SKIP_SUBTREE;
- }
- String entry = orgfile.getParentFile().toPath().relativize(dir).toString() + "/";
- LOGGER.trace("creating dir: " + entry);
- zos.putNextEntry(new ZipEntry(entry));
- zos.closeEntry();
- return FileVisitResult.CONTINUE;
- }
- });
- }
-
- zos.finish();
-
- } catch (IOException e) {
- IOUtils.closeQuietly(zos);
- throw new GenericFilebrowserException("Could not create zip file is allowed ", e);
- }
- // flushing the output
- out.flush();
-
- if (null != tempFile && config.isZipUseTempFile()) {
- IOUtils.closeQuietly(out);
- downloadFile(tempFile.getAbsolutePath(), response, "rename_me.zip", true);
- }
-
- } catch (Exception e) {
- LOGGER.error(e.getMessage(), e);
- throw new GenericFilebrowserException(e);
- } finally {
- if (null != tempFile && config.isZipUseTempFile()) {
- try {
- if(!tempFile.delete()) {
- tempFile.deleteOnExit();
- }
- } catch (Exception e2) {
- LOGGER.warn("could not delete tempfile " + tempFile.getAbsolutePath());
- tempFile.deleteOnExit();
- }
- }
- }
- }
-
- /**
- * checks if file is allowed for access
- *
- * @param path
- * @param write
- * @return
- * @throws IOException
- */
- protected boolean isAllowed(File path, boolean write) throws IOException {
- return isAllowed(path, write, config.isReadOnly());
- }
-
- @Override
- public String accessibleCSS(File file) {
- String res = "";
- if (!file.canRead()) {
- res += "not-readable";
- }
- if (!file.canWrite()) {
- res += " not-writeable";
- }
- if (file.isHidden()) {
- res += " file-hidden";
- }
- return res.trim();
- }
-
- @Override
- public String createFolder(String path, String folderName) throws IOException, GenericFilebrowserException {
- File file = new File(path);
- if (file.exists()) {
- if (isAllowed(file, true)) {
- file = new File(file, folderName);
- if(file.mkdirs()) {
- return file.getAbsolutePath();
- }
- throw new GenericFilebrowserException("could not create directories");
- }
- } else {
- LOGGER.warn("[createFolder] folder already exists: " + path);
- }
- return null;
- }
-
- @Override
- public String deleteResource(String path) throws IOException, GenericFilebrowserException {
- File file = new File(path);
-
- if (file.isDirectory() && !config.isDelteFolderAllowed()) {
- throw new GenericFilebrowserException("delete folder is not allowed");
- }
- if (file.isFile() && !config.isDelteFileAllowed()) {
- throw new GenericFilebrowserException("delete file is not allowed");
- }
- if (!isAllowed(file, true)) {
- throw new GenericFilebrowserException("delete "+(file.isDirectory() ? "folder" : "file")+" is not allowed");
- }
-
- String parent = file.getParent();
-
- if (file.isFile()) {
- FileUtils.deleteQuietly(file);
- } else {
- try {
- FileUtils.deleteDirectory(file);
- } catch (Exception e) {
- LOGGER.error("error deleting folder", e);
- }
- }
- return parent;
- }
-
- @Override
- public Map getFileInfo(String path) throws IOException {
- File file = new File(path);
- Map result = new TreeMap<>();
- result.put("file", file);
-
- if (file.isFile() && file.canRead() && config.getMaxFilesizeForHashes() > file.length()) {
- if (config.isInfoCrc32()) {
- result.put("file.checksumCRC32", FileUtils.checksumCRC32(file));
- }
-
- FileInputStream fis = new FileInputStream(file);
- if (config.isInfoMD5()) {
- result.put("file.md5Hex", DigestUtils.md5Hex(fis));
- }
- if (config.isInfoSha1()) {
- result.put("file.sha1Hex", DigestUtils.sha1Hex(fis));
- }
- if (config.isInfoSha256()) {
- result.put("file.sha256Hex", DigestUtils.sha256Hex(fis));
- }
-// result.put("file.sha384Hex", DigestUtils.sha384Hex(fis));
-// result.put("file.sha512Hex", DigestUtils.sha512Hex(fis));
-
- IOUtils.closeQuietly(fis);
- }
-
- result.put("file.lastModified", file.lastModified());
- result.put("file.canWrite", file.canWrite());
- result.put("file.canRead", file.canRead());
- result.put("file.canExecute", file.canExecute());
- result.put("file.isHidden", file.isHidden());
-
- result.put("disk.totalSpace", file.getTotalSpace());
- result.put("disk.usableSpace", file.getUsableSpace());
- result.put("disk.freeSpace", file.getFreeSpace());
- result.put("disk.totalSpace.coreFormat", getFileSize(file.getTotalSpace()));
- result.put("disk.usableSpace.coreFormat", getFileSize(file.getUsableSpace()));
- result.put("disk.freeSpace.coreFormat", getFileSize(file.getFreeSpace()));
- result.put("disk.totalSpace.commonFormat", getNormalFileSize(file.getTotalSpace()));
- result.put("disk.usableSpace.commonFormat", getNormalFileSize(file.getUsableSpace()));
- result.put("disk.freeSpace.commonFormat", getNormalFileSize(file.getFreeSpace()));
-
- String os = System.getProperty("os.name").toLowerCase();
- result.put("system.operationSystem", os);
-
- Class extends BasicFileAttributes> attributesClass =
- isWindows(os) ? DosFileAttributes.class : BasicFileAttributes.class;
- BasicFileAttributes attr = Files.readAttributes(file.toPath(), attributesClass);
-
- result.put("file.attr.creationTime", attr.creationTime());
- result.put("file.attr.lastAccessTime", attr.lastAccessTime());
- result.put("file.attr.lastModifiedTime", attr.lastModifiedTime());
-
- result.put("file.attr.isDirectory", attr.isDirectory());
- result.put("file.attr.isOther", attr.isOther());
- result.put("file.attr.isRegularFile", attr.isRegularFile());
- result.put("file.attr.isSymbolicLink", attr.isSymbolicLink());
- result.put("file.attr.size", attr.size());
-
-
- if (DosFileAttributes.class.isAssignableFrom(attr.getClass())) {
- result.put("file.attr.isArchive", ((DosFileAttributes)attr).isArchive());
- result.put("file.attr.isHidden", ((DosFileAttributes)attr).isHidden());
- result.put("file.attr.isReadOnly", ((DosFileAttributes)attr).isReadOnly());
- result.put("file.attr.isSystem", ((DosFileAttributes)attr).isSystem());
- }
-
- long size = file.length();
- if (file.isDirectory() && !attr.isSymbolicLink() && config.isCountFolderSize()) {
- size = FileUtils.sizeOfDirectory(file);
- }
- result.put("file.size", size);
- result.put("file.size.coreFormat", getFileSize(size));
- result.put("file.size.commonFormat", getNormalFileSize(size));
-
- if (!isWindows(os)) {
- Set permissions = Files.getPosixFilePermissions(file.toPath(), LinkOption.NOFOLLOW_LINKS);
- result.put("file.permissions", PosixFilePermissions.toString(permissions));
- }
-
- return result;
- }
-
- public static boolean isWindows(String os) {
- return (os.indexOf("win") >= 0);
- }
-
- @Override
- public boolean saveFile(String decodedPath, MultipartFile upload) throws IOException, GenericFilebrowserException {
- if (StringUtils.isEmpty(decodedPath)) {
- return false;
- }
- File uploadFolder = new File(decodedPath);
- if (uploadFolder.isDirectory() && isAllowed(uploadFolder, true)) {
-
- OutputStream fos = null;
- try {
- File file = new File(uploadFolder, upload.getOriginalFilename());
- fos = new FileOutputStream(file);
- long result = IOUtils.copyLarge(upload.getInputStream(), fos);
- LOGGER.info(String.format("uploaded %s bytes (%s)", result, getNormalFileSize(result)));
- } catch (Exception e) {
- LOGGER.error(e.getMessage(), e);
- return false;
- } finally {
- IOUtils.closeQuietly(fos);
- }
- return true;
- }
- throw new GenericFilebrowserException("upload not allowed");
- }
-}
+package de.chandre.admintool.filebrowser;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import de.chandre.admintool.core.utils.RegexUtil;
+
+/**
+ *
+ * @author Andre
+ * @since 1.0.1
+ */
+@Service("adminToolFilebrowserService")
+public class AdminToolFilebrowserServiceImpl extends AbstractFileBrowserService implements AdminToolFilebrowserService {
+
+ private static final Log LOGGER = LogFactory.getLog(AdminToolFilebrowserServiceImpl.class);
+
+ private static final Map FILE_SIZE_EXP = new TreeMap<>();
+ static {
+ FILE_SIZE_EXP.put(Integer.valueOf(1), " B");
+ FILE_SIZE_EXP.put(Integer.valueOf(2), " KB");
+ FILE_SIZE_EXP.put(Integer.valueOf(3), " MB");
+ FILE_SIZE_EXP.put(Integer.valueOf(4), " GB");
+ FILE_SIZE_EXP.put(Integer.valueOf(5), " TB");
+ FILE_SIZE_EXP.put(Integer.valueOf(6), " PB");
+ FILE_SIZE_EXP.put(Integer.valueOf(7), " EB");
+ }
+
+ @Autowired
+ private AdminToolFilebrowserConfig config;
+
+ @Autowired
+ private Environment env;
+
+ private Set rootDirsCache = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+
+ @Override
+ public Set getRootDirs() {
+ File[] roots = File.listRoots();
+
+ if (this.rootDirsCache.isEmpty()) {
+ for (File file : roots) {
+ if(!config.getForbiddenDrives().contains(file.getAbsolutePath().toLowerCase())) {
+ // if not forbidden add it to result
+ this.rootDirsCache.add(file.getAbsolutePath());
+ }
+ }
+ }
+ return this.rootDirsCache;
+ }
+
+ @Override
+ public boolean isRootActive(String rootDir, String currentDir) {
+ if (!StringUtils.isEmpty(currentDir) && currentDir.toLowerCase().startsWith(rootDir.toLowerCase())) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String getParent(String dir) throws IOException {
+ File file = new File(dir);
+ if (null != file.getParent() && isAllowed(file.getParentFile(), false)) {
+ return file.getParent();
+ }
+ return "";
+ }
+
+ protected List sort(File[] fileAr, final SortColumn sortCol, Boolean sortAsc) {
+ List files = Arrays.asList(fileAr);
+ if (null == sortCol) {
+ return files;
+ }
+ if (null == sortAsc) {
+ sortAsc = Boolean.TRUE;
+ }
+ final int direction = sortAsc.booleanValue() ? 1 : -1;
+
+ Collections.sort(files, new Comparator() {
+ @Override
+ public int compare(File o1, File o2) {
+ try {
+ switch (sortCol) {
+ case DATE:
+ return getLastChange(o1).compareTo(getLastChange(o2)) * direction;
+ case SIZE:
+ return Long.valueOf(o1.length()).compareTo(Long.valueOf(o2.length())) * direction;
+ case TYPE:
+ return getFileType(o1).compareTo(getFileType(o2)) * direction;
+ case NAME:
+ default:
+ return o1.getName().compareTo(o2.getName()) * direction;
+ }
+ } catch (Exception ignore) {
+ }
+
+ return 0;
+ }
+ });
+ return files;
+ }
+
+ @Override
+ public String getSortDirection(int current, SortColumn sortCol, Boolean sortAsc) {
+ if (current == sortCol.getIndex() && sortAsc != null) {
+ return sortAsc ? "up" : "down";
+ }
+ return "";
+ }
+
+ @Override
+ public List getDirectories(String currentDir, SortColumn sortCol, Boolean sortAsc, String filter) throws IOException {
+ File file = new File(currentDir);
+ if (null != file && isAllowed(file, false)) {
+ final Pattern fileNamePattern = getFileNamePattern(filter);
+ File[] files = file.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File dir) {
+ try {
+ if (isAllowed(dir, false) && dir.isDirectory()) {
+ if (null == fileNamePattern) {
+ return true;
+ } else if (null != fileNamePattern && fileNamePattern.matcher(dir.getName()).matches()) {
+ return true;
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.debug(e.getMessage(), e);
+ }
+ return false;
+ }
+ });
+ if (files != null) {
+ return sort(files, sortCol, sortAsc);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getFiles(String currentDir, SortColumn sortCol, Boolean sortAsc, String filter) throws IOException {
+ File file = new File(currentDir);
+ if (null != file && isAllowed(file, false)) {
+ final Pattern fileNamePattern = getFileNamePattern(filter);
+ File[] files = file.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File dir) {
+ try {
+ if (isAllowed(dir, false) && dir.isFile()) {
+ if (null == fileNamePattern) {
+ return true;
+ } else if (null != fileNamePattern && fileNamePattern.matcher(dir.getName()).matches()) {
+ return true;
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.debug(e.getMessage(), e);
+ }
+ return false;
+ }
+ });
+ if (files != null) {
+ return sort(files, sortCol, sortAsc);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ private Pattern getFileNamePattern(String filter) {
+ if (!StringUtils.isEmpty(filter)) {
+ return Pattern.compile(RegexUtil.wildcardToRegex(filter), Pattern.CASE_INSENSITIVE);
+ }
+ return null;
+ }
+
+ @Override
+ public String getDirOrRootName(File currentDir) {
+ if (StringUtils.isEmpty(currentDir.getName())) {
+ return currentDir.getAbsolutePath();
+ }
+ return currentDir.getName();
+ }
+
+ @Override
+ public List getBreadcrumb(String currentDir) {
+ if (null == currentDir) {
+ return Collections.emptyList();
+ }
+ List result = new ArrayList<>();
+ File file = new File(currentDir);
+ getParentsRecursive(file, result);
+ return result;
+ }
+
+ private void getParentsRecursive(File actual, List files) {
+ if (null != actual.getParentFile()) {
+ getParentsRecursive(actual.getParentFile(), files);
+ }
+ if (actual.isDirectory()) {
+ files.add(actual);
+ }
+ }
+
+
+
+ @Override
+ public String getFileSizeSum(String dir, String filter) throws IOException {
+ List files = getFiles(dir, null, true, filter);
+ Long res = files.stream().collect(Collectors.summingLong(File::length));
+ return String.format("%s in %s files", getFileSize(res), files.size());
+ }
+
+ @Override
+ public Date getLastChange(File file) throws IOException {
+ if (null != file) {
+ FileTime time = Files.getLastModifiedTime(file.toPath(), new LinkOption[]{});
+ return new Date(time.toMillis());
+ }
+ return new Date();
+ }
+
+ @Override
+ public String getFileType(File file) {
+ if (null == file) {
+ return "";
+ }
+ if (file.isDirectory()) {
+ return "DIR";
+ }
+ if (file.getName().lastIndexOf('.') == -1) {
+ return "";
+ }
+ return getExtension(file);
+ }
+
+ @Override
+ public String getFileSize(File file) {
+ return getFileSize(file.length());
+ }
+
+ /**
+ * returns the the size in B, KB, MB or GB depending on the length
+ *
+ * @param fileLength
+ * @return
+ */
+ @Override
+ public String getFileSize(long fileLength) {
+ return calculateDisplayFileSize(fileLength, config.getSizeDivisorMultiplicator());
+ }
+
+ @Override
+ public String getNormalFileSize(long fileLength) {
+ return calculateDisplayFileSize(fileLength, FileUtils.ONE_KB);
+ }
+
+ protected String calculateDisplayFileSize(long fileLength, long sizeDivisorMultiplicator) {
+ BigInteger multiplicator = BigInteger.valueOf(sizeDivisorMultiplicator);
+ BigInteger size = BigInteger.valueOf(fileLength);
+
+ for (Entry entry : FILE_SIZE_EXP.entrySet()) {
+ BigInteger divisor = multiplicator.pow(entry.getKey().intValue());
+ if (fileLength < divisor.longValue()) {
+ if (entry.getKey().intValue() == 1) {
+ return String.valueOf(size) + entry.getValue();
+ }
+ return formatFileSize(size, multiplicator.pow(entry.getKey().intValue() - 1), entry.getValue());
+ }
+ }
+ //should not happen
+ return FileUtils.byteCountToDisplaySize(fileLength);
+ }
+
+ /**
+ * calculates the and formats files size
+ * @see #getFileSize(long)
+ * @param fileLength
+ * @param divisor
+ * @param unit the Unit for the divisor
+ * @return
+ */
+ protected String formatFileSize(BigInteger fileLength, BigInteger divisor, String unit) {
+ BigDecimal size = new BigDecimal(fileLength);
+ size = size.setScale(config.getFileSizeDisplayScale()).divide(new BigDecimal(divisor), BigDecimal.ROUND_HALF_EVEN);
+ return String.format("%s %s", size.doubleValue(), unit);
+ }
+
+ /**
+ *
+ * @param fileName the name
+ * @param size (optional) size/length of content
+ * @param response the servlet response
+ */
+ protected void prepareDownloadResponse(String fileName, Long size, HttpServletResponse response, boolean asAttachment) {
+
+ String mimeType = MimeTypes.getMimeType(getExtension(fileName));
+ LOGGER.info("following mimeType:" + mimeType);
+ if (asAttachment || null == mimeType) {
+ response.setContentType("application/octet-stream");
+ response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
+ } else {
+ response.setContentType(mimeType);
+ response.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
+ }
+ if (null != size) {
+ response.setContentLength(size.intValue());
+ }
+ }
+
+ @Override
+ public void downloadFile(String filePath, HttpServletResponse response, boolean asAttachment) throws DownloadNotAllowedException, GenericFilebrowserException {
+ downloadFile(filePath, response, null, asAttachment);
+ }
+
+ @Override
+ public void downloadFile(String filePath, HttpServletResponse response, String alternativeFileName, boolean asAttachment) throws DownloadNotAllowedException, GenericFilebrowserException {
+ File file = new File(filePath);
+ try {
+ if (!isAllowed(file, false)) {
+ throw new DownloadNotAllowedException();
+ }
+ } catch (IOException e) {
+ throw new GenericFilebrowserException(e);
+ }
+
+ prepareDownloadResponse(StringUtils.isEmpty(alternativeFileName) ? file.getName() : alternativeFileName,
+ Long.valueOf(file.length()), response, asAttachment);
+
+ InputStream in = null;
+ BufferedInputStream fileInput = null;
+ try {
+ in = new FileInputStream(file);
+ fileInput = new BufferedInputStream(in);
+ ServletOutputStream out = response.getOutputStream();
+ IOUtils.copy(fileInput, out, 8192);
+ out.flush();
+ } catch (IOException e) {
+ throw new GenericFilebrowserException("could not prepare file for downloading", e);
+ }
+ finally {
+ IOUtils.closeQuietly(fileInput);
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ @Override
+ public void downloadFilesAsZip(List filePaths, HttpServletResponse response) throws GenericFilebrowserException {
+
+ File tempFile = null;
+ try {
+ OutputStream out = null;
+
+ try {
+ if (config.isZipUseTempFile()) {
+ String tempDirConf = config.getZipTempDir();
+ File tempDir = null;
+ if (StringUtils.isEmpty(tempDirConf)) {
+ tempFile = File.createTempFile("zip", null);
+ }
+ else if (tempDirConf.startsWith("sys")) {
+ tempDir = new File(System.getProperty(tempDirConf.substring(4, tempDirConf.length())));
+ }
+ else if (tempDirConf.startsWith("env")) {
+ tempDir = new File(env.getProperty(tempDirConf.substring(4, tempDirConf.length())));
+ }
+ else {
+ tempDir = new File(tempDirConf);
+ }
+ if (null == tempFile) {
+ tempFile = File.createTempFile("zip", null, tempDir);
+ }
+ out = new FileOutputStream(tempFile);
+ }
+ } catch (Exception e) {
+ LOGGER.warn("could not create temporary file, using servlet outputstream for serving ZIP");
+ }
+
+ if (null == out) {
+ tempFile = null;
+ prepareDownloadResponse("rename_me.zip", null, response, true);
+ out = response.getOutputStream();
+ }
+
+ ZipOutputStream zos = new ZipOutputStream(out);
+
+ zos.setLevel(config.getZipCompessionLevel());
+ try {
+ for (String filePathStr : filePaths) {
+ File orgfile = new File(filePathStr);
+ Files.walkFileTree(orgfile.toPath(), new SimpleFileVisitor() {
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (!isAllowed(file.toFile(), false)) {
+ LOGGER.debug("ZIP creation: skipping not allowed file: " + file.toAbsolutePath());
+ return FileVisitResult.CONTINUE;
+ }
+ String entry = orgfile.getParentFile().toPath().relativize(file).toString();
+ LOGGER.trace("creating entry: " + entry);
+ zos.putNextEntry(new ZipEntry(entry));
+ Files.copy(file, zos);
+ zos.closeEntry();
+ return FileVisitResult.CONTINUE;
+ }
+
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ if (!isAllowed(dir.toFile(), false)) {
+ LOGGER.debug("ZIP creation: skipping not allowed directory and subtree for: " + dir.toAbsolutePath());
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ String entry = orgfile.getParentFile().toPath().relativize(dir).toString() + "/";
+ LOGGER.trace("creating dir: " + entry);
+ zos.putNextEntry(new ZipEntry(entry));
+ zos.closeEntry();
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ zos.finish();
+
+ } catch (IOException e) {
+ IOUtils.closeQuietly(zos);
+ throw new GenericFilebrowserException("Could not create zip file is allowed ", e);
+ }
+ // flushing the output
+ out.flush();
+
+ if (null != tempFile && config.isZipUseTempFile()) {
+ IOUtils.closeQuietly(out);
+ downloadFile(tempFile.getAbsolutePath(), response, "rename_me.zip", true);
+ }
+
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new GenericFilebrowserException(e);
+ } finally {
+ if (null != tempFile && config.isZipUseTempFile()) {
+ try {
+ if(!tempFile.delete()) {
+ tempFile.deleteOnExit();
+ }
+ } catch (Exception e2) {
+ LOGGER.warn("could not delete tempfile " + tempFile.getAbsolutePath());
+ tempFile.deleteOnExit();
+ }
+ }
+ }
+ }
+
+ /**
+ * checks if file is allowed for access
+ *
+ * @param path
+ * @param write
+ * @return
+ * @throws IOException
+ */
+ protected boolean isAllowed(File path, boolean write) throws IOException {
+ return isAllowed(path, write, config.isReadOnly());
+ }
+
+ @Override
+ public String accessibleCSS(File file) {
+ String res = "";
+ if (!file.canRead()) {
+ res += "not-readable";
+ }
+ if (!file.canWrite()) {
+ res += " not-writeable";
+ }
+ if (file.isHidden()) {
+ res += " file-hidden";
+ }
+ return res.trim();
+ }
+
+ @Override
+ public String createFolder(String path, String folderName) throws IOException, GenericFilebrowserException {
+ if (StringUtils.isEmpty(path) && StringUtils.isEmpty(folderName)) {
+ throw new GenericFilebrowserException("Nothing to create.");
+ }
+ File file = new File(path);
+ if (file.exists()) {
+ if (isAllowed(file, true)) {
+ file = new File(file, folderName);
+ if(file.mkdirs()) {
+ return file.getAbsolutePath();
+ }
+ throw new GenericFilebrowserException("Could not create directories.");
+ }
+ } else {
+ LOGGER.warn("[createFolder] folder already exists: " + path);
+ }
+ return null;
+ }
+
+ @Override
+ public String deleteResource(String path) throws IOException, GenericFilebrowserException {
+ if (StringUtils.isEmpty(path)) {
+ throw new GenericFilebrowserException("Nothing to delete.");
+ }
+ File file = new File(path);
+
+ if (file.isDirectory() && !config.isDelteFolderAllowed()) {
+ throw new GenericFilebrowserException("Delete folders functionality is deactivated.");
+ }
+ if (file.isFile() && !config.isDelteFileAllowed()) {
+ throw new GenericFilebrowserException("Delete files functionality is deactivated.");
+ }
+ if (!isAllowed(file, true)) {
+ throw new GenericFilebrowserException("Delete "+(file.isDirectory() ? "folder" : "file")+" is not allowed.");
+ }
+ if (!file.canWrite() && config.isNotDeletableIfNotWriteable()) {
+ throw new GenericFilebrowserException(
+ "Deleting " +(file.isDirectory() ? "folder" : "file") + " '" + file.getName() + "' is not allowed.");
+ }
+
+ String parent = file.getParent();
+
+ if (file.isFile()) {
+ FileUtils.deleteQuietly(file);
+ } else {
+ try {
+ FileUtils.deleteDirectory(file);
+ } catch (Exception e) {
+ LOGGER.error("error deleting folder", e);
+ }
+ }
+ return parent;
+ }
+
+ @Override
+ public Map getFileInfo(String path) throws IOException {
+ File file = new File(path);
+ Map result = new TreeMap<>();
+ result.put("file", file);
+
+ if (file.isFile() && file.canRead() && config.getMaxFilesizeForHashes() > file.length()) {
+ if (config.isInfoCrc32()) {
+ result.put("file.checksumCRC32", FileUtils.checksumCRC32(file));
+ }
+
+ FileInputStream fis = new FileInputStream(file);
+ if (config.isInfoMD5()) {
+ result.put("file.md5Hex", DigestUtils.md5Hex(fis));
+ }
+ if (config.isInfoSha1()) {
+ result.put("file.sha1Hex", DigestUtils.sha1Hex(fis));
+ }
+ if (config.isInfoSha256()) {
+ result.put("file.sha256Hex", DigestUtils.sha256Hex(fis));
+ }
+// result.put("file.sha384Hex", DigestUtils.sha384Hex(fis));
+// result.put("file.sha512Hex", DigestUtils.sha512Hex(fis));
+
+ IOUtils.closeQuietly(fis);
+ }
+
+ result.put("file.lastModified", file.lastModified());
+ result.put("file.canWrite", file.canWrite());
+ result.put("file.canRead", file.canRead());
+ result.put("file.canExecute", file.canExecute());
+ result.put("file.isHidden", file.isHidden());
+
+ result.put("disk.totalSpace", file.getTotalSpace());
+ result.put("disk.usableSpace", file.getUsableSpace());
+ result.put("disk.freeSpace", file.getFreeSpace());
+ result.put("disk.totalSpace.coreFormat", getFileSize(file.getTotalSpace()));
+ result.put("disk.usableSpace.coreFormat", getFileSize(file.getUsableSpace()));
+ result.put("disk.freeSpace.coreFormat", getFileSize(file.getFreeSpace()));
+ result.put("disk.totalSpace.commonFormat", getNormalFileSize(file.getTotalSpace()));
+ result.put("disk.usableSpace.commonFormat", getNormalFileSize(file.getUsableSpace()));
+ result.put("disk.freeSpace.commonFormat", getNormalFileSize(file.getFreeSpace()));
+
+ String os = System.getProperty("os.name").toLowerCase();
+ result.put("system.operationSystem", os);
+
+ Class extends BasicFileAttributes> attributesClass =
+ isWindows(os) ? DosFileAttributes.class : BasicFileAttributes.class;
+ BasicFileAttributes attr = Files.readAttributes(file.toPath(), attributesClass);
+
+ result.put("file.attr.creationTime", attr.creationTime());
+ result.put("file.attr.lastAccessTime", attr.lastAccessTime());
+ result.put("file.attr.lastModifiedTime", attr.lastModifiedTime());
+
+ result.put("file.attr.isDirectory", attr.isDirectory());
+ result.put("file.attr.isOther", attr.isOther());
+ result.put("file.attr.isRegularFile", attr.isRegularFile());
+ result.put("file.attr.isSymbolicLink", attr.isSymbolicLink());
+ result.put("file.attr.size", attr.size());
+
+
+ if (DosFileAttributes.class.isAssignableFrom(attr.getClass())) {
+ result.put("file.attr.isArchive", ((DosFileAttributes)attr).isArchive());
+ result.put("file.attr.isHidden", ((DosFileAttributes)attr).isHidden());
+ result.put("file.attr.isReadOnly", ((DosFileAttributes)attr).isReadOnly());
+ result.put("file.attr.isSystem", ((DosFileAttributes)attr).isSystem());
+ }
+
+ long size = file.length();
+ if (file.isDirectory() && !attr.isSymbolicLink() && config.isCountFolderSize()) {
+ size = FileUtils.sizeOfDirectory(file);
+ }
+ result.put("file.size", size);
+ result.put("file.size.coreFormat", getFileSize(size));
+ result.put("file.size.commonFormat", getNormalFileSize(size));
+
+ if (!isWindows(os)) {
+ Set permissions = Files.getPosixFilePermissions(file.toPath(), LinkOption.NOFOLLOW_LINKS);
+ result.put("file.permissions", PosixFilePermissions.toString(permissions));
+ }
+
+ return result;
+ }
+
+ public static boolean isWindows(String os) {
+ return (os.indexOf("win") >= 0);
+ }
+
+ @Override
+ public boolean saveFile(String decodedPath, MultipartFile upload) throws IOException, GenericFilebrowserException {
+ if (StringUtils.isEmpty(decodedPath)) {
+ return false;
+ }
+ File uploadFolder = new File(decodedPath);
+ if (uploadFolder.isDirectory() && isAllowed(uploadFolder, true)) {
+
+ OutputStream fos = null;
+ try {
+ File file = new File(uploadFolder, upload.getOriginalFilename());
+ fos = new FileOutputStream(file);
+ long result = IOUtils.copyLarge(upload.getInputStream(), fos);
+ LOGGER.info(String.format("uploaded %s bytes (%s)", result, getNormalFileSize(result)));
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ return false;
+ } finally {
+ IOUtils.closeQuietly(fos);
+ }
+ return true;
+ }
+ throw new GenericFilebrowserException("upload not allowed");
+ }
+}
diff --git a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerController.java b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerController.java
index 05effde..f3bdc16 100644
--- a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerController.java
+++ b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerController.java
@@ -1,82 +1,120 @@
-package de.chandre.admintool.fileviewer;
-
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-
-import de.chandre.admintool.core.AdminTool;
-import de.chandre.admintool.core.controller.AbstractAdminController;
-import de.chandre.admintool.filebrowser.AdminToolFilebrowserLoader;
-import de.chandre.admintool.filebrowser.GenericFilebrowserException;
-
-/**
- * Fileviewer controller
- * requires admintool-core 1.0.1
- * @author Andre
- * @since 1.0.1
- */
-@Controller
-@RequestMapping(AdminTool.ROOTCONTEXT + "/fileviewer")
-public class AdminToolFileviewerController extends AbstractAdminController {
-
- private static final Log LOGGER = LogFactory.getLog(AdminToolFileviewerController.class);
-
- @Autowired
- private AdminToolFileviewerService filebrowserService;
-
- @Autowired
- private AdminToolFileviewerConfig fileviewerConfig;
-
-
- @RequestMapping(value = {"/show",}, method={RequestMethod.GET, RequestMethod.POST})
- public String loadFile(@RequestParam("file") String file, @RequestParam(name="encoding", required=false) String encoding,
- ModelMap model, HttpServletRequest request) throws GenericFilebrowserException, UnsupportedEncodingException {
- if (!fileviewerConfig.isEnabled()) {
- return null;
- }
- if(LOGGER.isTraceEnabled()) LOGGER.trace("serving file viewer page for file: " + file + ", encoding: " + encoding);
- String templatePath = addCommonContextVars(model, request, "filebrowser", AdminToolFilebrowserLoader.TARGET_FILEVIEWER);
-
- String decodedPath = URLDecoder.decode(file, "UTF-8");
- File currentFile = new File(decodedPath);
- model.put("currentDir", currentFile.getParent());
-
- filebrowserService.isFileAllowed(currentFile, false);
-
- model.put("currentFile", currentFile);
- model.put("selEncoding", StringUtils.isEmpty(encoding) ? fileviewerConfig.getDefaultEncoding() : encoding);
-
- return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
- }
-
- @RequestMapping(value = {"/update",}, method={RequestMethod.POST})
- public String updateFile(@RequestParam("file") String file, @RequestParam(name="encoding", required=false) String encoding,
- @RequestParam("fileContent") String fileContent,
- ModelMap model, HttpServletRequest request) throws GenericFilebrowserException {
- if (!fileviewerConfig.isEnabled()) {
- return null;
- }
- if(LOGGER.isTraceEnabled()) LOGGER.trace("updating file: " + file + ", encoding: " + encoding);
- String templatePath = addCommonContextVars(model, request, "filebrowser", null);
-
- File currentFile = new File(file);
- model.put("currentDir", currentFile.getParent());
-
- filebrowserService.writeStringToFile(currentFile, encoding, fileContent);
-
- return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
- }
-
-}
+package de.chandre.admintool.fileviewer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.ModelAndView;
+
+import de.chandre.admintool.core.AdminTool;
+import de.chandre.admintool.core.controller.AbstractAdminController;
+import de.chandre.admintool.filebrowser.AdminToolFilebrowserConfig;
+import de.chandre.admintool.filebrowser.AdminToolFilebrowserLoader;
+import de.chandre.admintool.filebrowser.AdminToolFilebrowserService;
+import de.chandre.admintool.filebrowser.DownloadNotAllowedException;
+import de.chandre.admintool.filebrowser.GenericFilebrowserException;
+
+/**
+ * Fileviewer controller
+ * requires admintool-core 1.0.1
+ * @author Andre
+ * @since 1.0.1
+ */
+@Controller
+@RequestMapping(AdminTool.ROOTCONTEXT + "/fileviewer")
+public class AdminToolFileviewerController extends AbstractAdminController {
+
+ private static final Log LOGGER = LogFactory.getLog(AdminToolFileviewerController.class);
+
+ @Autowired
+ private AdminToolFileviewerService fileviewerService;
+
+ @Autowired
+ private AdminToolFilebrowserService filebrowserService;
+
+ @Autowired
+ private AdminToolFileviewerConfig fileviewerConfig;
+
+ @Autowired
+ private AdminToolFilebrowserConfig filebrowserConfig;
+
+
+ @RequestMapping(value = {"/show",}, method={RequestMethod.GET, RequestMethod.POST})
+ public String loadFile(@RequestParam("file") String file, @RequestParam(name="encoding", required=false) String encoding,
+ ModelMap model, HttpServletRequest request) throws GenericFilebrowserException, UnsupportedEncodingException {
+ if (!fileviewerConfig.isEnabled()) {
+ return null;
+ }
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("serving file viewer page for file: " + file + ", encoding: " + encoding);
+ String templatePath = addCommonContextVars(model, request, "filebrowser", AdminToolFilebrowserLoader.TARGET_FILEVIEWER);
+
+ String decodedPath = URLDecoder.decode(file, "UTF-8");
+ File currentFile = new File(decodedPath);
+ model.put("currentDir", currentFile.getParent());
+
+ fileviewerService.isFileAllowed(currentFile, false);
+
+ model.put("currentFile", currentFile);
+ model.put("selEncoding", StringUtils.isEmpty(encoding) ? fileviewerConfig.getDefaultEncoding() : encoding);
+
+ return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
+ }
+
+ @RequestMapping(value = {"/update",}, method={RequestMethod.POST})
+ public String updateFile(@RequestParam("file") String file, @RequestParam(name="encoding", required=false) String encoding,
+ @RequestParam("fileContent") String fileContent,
+ ModelMap model, HttpServletRequest request) throws GenericFilebrowserException {
+ if (!fileviewerConfig.isEnabled()) {
+ return null;
+ }
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("updating file: " + file + ", encoding: " + encoding);
+ String templatePath = addCommonContextVars(model, request, "filebrowser", null);
+
+ File currentFile = new File(file);
+ model.put("currentDir", currentFile.getParent());
+
+ fileviewerService.writeStringToFile(currentFile, encoding, fileContent);
+
+ return AdminTool.ROOTCONTEXT_NAME + AdminTool.SLASH + templatePath;
+ }
+
+ @ExceptionHandler({DownloadNotAllowedException.class, GenericFilebrowserException.class})
+ public ModelAndView handleException(Exception exception, HttpServletRequest request) throws IOException {
+ if(LOGGER.isTraceEnabled()) LOGGER.trace("handleException: " + exception.getMessage());
+
+ ModelAndView mv = new ModelAndView(AdminTool.GENERIC_ERROR_TPL_PATH);
+ addCommonContextVars(mv.getModelMap(), request, "filebrowser", null);
+
+ String lastFile = request.getParameter("file");
+ if (StringUtils.isEmpty(lastFile)) {
+ lastFile = request.getParameter("selectedFile");
+ }
+ String decodedPath = null;
+ if (StringUtils.hasLength(lastFile)) {
+ decodedPath = URLDecoder.decode(lastFile, "UTF-8");
+ }
+ LOGGER.info("lastFile: " + lastFile);
+ if (StringUtils.hasLength(decodedPath) &&
+ filebrowserService.isAllowed(new File(decodedPath).getParentFile(), false, filebrowserConfig.isReadOnly()) ) {
+ mv.getModelMap().put("currentDir", new File(decodedPath).getParent());
+ } else {
+ mv.getModelMap().put("currentDir", filebrowserConfig.getStartDir().getAbsolutePath());
+ }
+ mv.getModelMap().put("exceptionMessage", exception.getMessage());
+ return mv;
+ }
+
+}
diff --git a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerServiceImpl.java b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerServiceImpl.java
index e29f36f..cab012a 100644
--- a/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerServiceImpl.java
+++ b/admin-tools-filebrowser/src/main/java/de/chandre/admintool/fileviewer/AdminToolFileviewerServiceImpl.java
@@ -1,77 +1,78 @@
-package de.chandre.admintool.fileviewer;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.commons.io.FileUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-
-import de.chandre.admintool.filebrowser.AbstractFileBrowserService;
-import de.chandre.admintool.filebrowser.GenericFilebrowserException;
-
-/**
- *
- * @author Andre
- * @since 1.0.1
- */
-@Service("adminToolFileviewerService")
-public class AdminToolFileviewerServiceImpl extends AbstractFileBrowserService implements AdminToolFileviewerService {
-
- @Autowired
- private AdminToolFileviewerConfig config;
-
- @Override
- public void isFileAllowed(File file, boolean write) throws GenericFilebrowserException {
- try {
- if (!config.isEnabled() || !isAllowed(file, write, config.isReadOnly()) || !isExtensionAllowedAndWriteable(file)) {
- throw new GenericFilebrowserException("insufficient file permissions");
- }
- } catch (IOException e) {
- throw new GenericFilebrowserException("Error while try to check file permission: " + e.getMessage(), e);
- }
- }
-
- @Override
- public boolean isExtensionAllowedAndReadable(File file) {
- if (null == file || !config.isEnabled() || file.isDirectory() || !file.canRead()) {
- return false;
- }
- if (config.getAllowedExtensions().contains(getExtension(file))) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean isExtensionAllowedAndWriteable(File file) {
- if (!config.isReadOnly() && isExtensionAllowedAndReadable(file) && file.canWrite()) {
- return config.getAllowedExtensionsToEdit().contains(getExtension(file));
- }
- return false;
- }
-
- @Override
- public String readFileToString(File file, String encoding) throws IOException {
- return FileUtils.readFileToString(file, (StringUtils.isEmpty(encoding) ? config.getDefaultEncoding() : encoding));
- }
-
- @Override
- public boolean isChangeable(File file) {
- if (isExtensionAllowedAndWriteable(file)) {
- return true;
- }
- return false;
- }
-
- @Override
- public void writeStringToFile(File file, String encoding, String fileContent) throws GenericFilebrowserException {
- isFileAllowed(file, true);
- try {
- FileUtils.writeStringToFile(file, fileContent, encoding, false);
- } catch (Exception e) {
- throw new GenericFilebrowserException("could not write content to file: " + e.getMessage(), e);
- }
- }
-}
+package de.chandre.admintool.fileviewer;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import de.chandre.admintool.filebrowser.AbstractFileBrowserService;
+import de.chandre.admintool.filebrowser.GenericFilebrowserException;
+
+/**
+ *
+ * @author Andre
+ * @since 1.0.1
+ */
+@Service("adminToolFileviewerService")
+public class AdminToolFileviewerServiceImpl extends AbstractFileBrowserService implements AdminToolFileviewerService {
+
+ @Autowired
+ private AdminToolFileviewerConfig config;
+
+ @Override
+ public void isFileAllowed(File file, boolean write) throws GenericFilebrowserException {
+ try {
+ if (!config.isEnabled() || !isAllowed(file, write, config.isReadOnly()) || !isExtensionAllowedAndReadable(file)
+ || (write && !isExtensionAllowedAndWriteable(file))) {
+ throw new GenericFilebrowserException("insufficient file permissions");
+ }
+ } catch (IOException e) {
+ throw new GenericFilebrowserException("Error while try to check file permission: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public boolean isExtensionAllowedAndReadable(File file) {
+ if (null == file || !config.isEnabled() || file.isDirectory() || !file.canRead()) {
+ return false;
+ }
+ if (config.getAllowedExtensions().contains(getExtension(file))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isExtensionAllowedAndWriteable(File file) {
+ if (!config.isReadOnly() && isExtensionAllowedAndReadable(file) && file.canWrite()) {
+ return config.getAllowedExtensionsToEdit().contains(getExtension(file));
+ }
+ return false;
+ }
+
+ @Override
+ public String readFileToString(File file, String encoding) throws IOException {
+ return FileUtils.readFileToString(file, (StringUtils.isEmpty(encoding) ? config.getDefaultEncoding() : encoding));
+ }
+
+ @Override
+ public boolean isChangeable(File file) {
+ if (isExtensionAllowedAndWriteable(file)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void writeStringToFile(File file, String encoding, String fileContent) throws GenericFilebrowserException {
+ isFileAllowed(file, true);
+ try {
+ FileUtils.writeStringToFile(file, fileContent, encoding, false);
+ } catch (Exception e) {
+ throw new GenericFilebrowserException("could not write content to file: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/admin-tools-filebrowser/src/main/resources/static/admintool/filebrowser/js/filebrowser.js b/admin-tools-filebrowser/src/main/resources/static/admintool/filebrowser/js/filebrowser.js
index dfe3965..fbef258 100644
--- a/admin-tools-filebrowser/src/main/resources/static/admintool/filebrowser/js/filebrowser.js
+++ b/admin-tools-filebrowser/src/main/resources/static/admintool/filebrowser/js/filebrowser.js
@@ -1,69 +1,72 @@
-var allFilesSelected = false;
-var allDirsSelected = false;
-
-$( document ).ready(function() {
-
- $('#selectFiles').on('click', function() {
- $('input.file').each(function() {
- $(this).prop( "checked", !allFilesSelected )
- });
- allFilesSelected = !allFilesSelected;
- });
-
- $('#selectDirs').on('click', function() {
- $('input.dir').each(function() {
- $(this).prop( "checked", !allDirsSelected )
- });
- allDirsSelected = !allDirsSelected;
- });
-
- $('#createDir').on('click', function() {
-
- getByID("createFolderModal").modal();
- });
-
- $('#uploadFile').on('click', function() {
-
- var csrf = {};
- csrf[getCSRFHeader()] = getCSRFToken();
- var uploader = null;
- uploader = new qq.FineUploader({
- element: $("#fine-uploader")[0],
- request: {
- endpoint: getWebContext() + '/admintool/filebrowser/upload',
- customHeaders: csrf,
- params: {
- "currentDir" : $("#currentDir").text()
- }
- }
- });
-
- getByID("uploadModal").modal();
- });
-
- $('.delete').each(function() {
- var btn = $(this);
- btn.on('click', function() {
- var clickedBtn = $(this);
- $("#resourceToDeleteShown").text(decodeURIComponent(decodeURI(clickedBtn.data("resource"))));
- $("#resourceToDelete").val(clickedBtn.data("resource"));
- getByID("deleteConfirmModal").modal();
- });
- });
-
- $('.infoBtn').each(function() {
- var btn = $(this);
- btn.on('click', function() {
- var link = $(this);
-
- sendRequest("/admintool/filebrowser/info?file=" + link.data('path'), "GET", "text", function(data) {
- var div = getByID('infoModals');
- div.html(data);
- div.modal();
- });
-
- });
- });
-
-
+var allFilesSelected = false;
+var allDirsSelected = false;
+
+$( document ).ready(function() {
+
+ $('#selectFiles').on('click', function() {
+ $('input.file').each(function() {
+ $(this).prop( "checked", !allFilesSelected )
+ });
+ allFilesSelected = !allFilesSelected;
+ });
+
+ $('#selectDirs').on('click', function() {
+ $('input.dir').each(function() {
+ $(this).prop( "checked", !allDirsSelected )
+ });
+ allDirsSelected = !allDirsSelected;
+ });
+
+ $('#createDir').on('click', function() {
+
+ getByID("createFolderModal").modal();
+ });
+
+ $('#uploadFile').on('click', function() {
+
+ var csrf = {};
+ csrf[getCSRFHeader()] = getCSRFToken();
+ var uploader = null;
+ uploader = new qq.FineUploader({
+ element: $("#fine-uploader")[0],
+ request: {
+ endpoint: getWebContext() + '/admintool/filebrowser/upload',
+ customHeaders: csrf,
+ params: {
+ "currentDir" : $("#currentDir").text()
+ }
+ }
+ });
+ var uploadModal = getByID("uploadModal");
+ uploadModal.modal();
+ uploadModal.on('hidden.bs.modal', function () {
+ location.reload();
+ });
+ });
+
+ $('.delete').each(function() {
+ var btn = $(this);
+ btn.on('click', function() {
+ var clickedBtn = $(this);
+ $("#resourceToDeleteShown").text(decodeURIComponent(decodeURI(clickedBtn.data("resource"))));
+ $("#resourceToDelete").val(clickedBtn.data("resource"));
+ getByID("deleteConfirmModal").modal();
+ });
+ });
+
+ $('.infoBtn').each(function() {
+ var btn = $(this);
+ btn.on('click', function() {
+ var link = $(this);
+
+ sendRequest("/admintool/filebrowser/info?file=" + link.data('path'), "GET", "text", function(data) {
+ var div = getByID('infoModals');
+ div.html(data);
+ div.modal();
+ });
+
+ });
+ });
+
+
});
\ No newline at end of file
diff --git a/admin-tools-jminix/README.md b/admin-tools-jminix/README.md
index a3da8ca..5df30ac 100644
--- a/admin-tools-jminix/README.md
+++ b/admin-tools-jminix/README.md
@@ -12,12 +12,12 @@
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-jminix
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-log4j2/README.md b/admin-tools-log4j2/README.md
index 5863b60..60e0861 100644
--- a/admin-tools-log4j2/README.md
+++ b/admin-tools-log4j2/README.md
@@ -15,12 +15,12 @@
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-log4j2
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-melody/README.md b/admin-tools-melody/README.md
index 3b608c3..4fb6298 100644
--- a/admin-tools-melody/README.md
+++ b/admin-tools-melody/README.md
@@ -22,12 +22,12 @@ http
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-melody
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-properties/README.md b/admin-tools-properties/README.md
index 7806825..1ac7275 100644
--- a/admin-tools-properties/README.md
+++ b/admin-tools-properties/README.md
@@ -12,12 +12,12 @@
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-properties
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-quartz/README.md b/admin-tools-quartz/README.md
index d340a75..0f32d93 100644
--- a/admin-tools-quartz/README.md
+++ b/admin-tools-quartz/README.md
@@ -22,12 +22,12 @@
de.chandre.admin-tools
admin-tools-core
- 1.1.6.1
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-quartz
- 1.1.6.1
+ 1.1.6.2
```
diff --git a/admin-tools-security/admin-tools-security-simple/README.md b/admin-tools-security/admin-tools-security-simple/README.md
index 8a10377..f995b28 100644
--- a/admin-tools-security/admin-tools-security-simple/README.md
+++ b/admin-tools-security/admin-tools-security-simple/README.md
@@ -19,12 +19,12 @@ Until version 1.1.6 the following dependencies must be used.
de.chandre.admin-tools
admin-tools-core
- 1.1.6
+ 1.1.6.2
de.chandre.admin-tools
admin-tools-core-security
- 1.1.6
+ 1.1.6.2
```