1,102 → 1,49 |
/* |
* Copyright 2013 Brian Rosenberger (Brutex Network) |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
* You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software |
* distributed under the License is distributed on an "AS IS" BASIS, |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
* See the License for the specific language governing permissions and |
* limitations under the License. |
*/ |
|
package net.brutex.xservices.ws.rs; |
|
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.OutputStream; |
import java.net.URI; |
import java.nio.file.DirectoryStream; |
import java.nio.file.FileSystems; |
import java.nio.file.FileVisitOption; |
import java.nio.file.FileVisitResult; |
import java.nio.file.Files; |
import java.nio.file.Path; |
import java.nio.file.PathMatcher; |
import java.nio.file.SimpleFileVisitor; |
import java.nio.file.attribute.BasicFileAttributeView; |
import java.nio.file.attribute.BasicFileAttributes; |
import java.io.FileFilter; |
import java.util.ArrayList; |
import java.util.EnumSet; |
import java.util.List; |
import java.util.zip.ZipEntry; |
import java.util.zip.ZipOutputStream; |
|
import javax.ws.rs.NotAuthorizedException; |
import javax.ws.rs.WebApplicationException; |
import javax.ws.rs.core.GenericEntity; |
import javax.ws.rs.core.HttpHeaders; |
import javax.ws.rs.core.MediaType; |
import javax.ws.rs.core.Response; |
import javax.ws.rs.core.StreamingOutput; |
import javax.ws.rs.core.UriInfo; |
|
import net.brutex.xservices.security.DirectoryPermission; |
import net.brutex.xservices.security.StandardSecurityManager; |
import net.brutex.xservices.security.UserIdentity; |
import net.brutex.xservices.types.FileInfoType; |
import net.brutex.xservices.util.FileWalker; |
|
import org.apache.jcs.JCS; |
import org.apache.jcs.access.exception.CacheException; |
import org.apache.log4j.Logger; |
import org.apache.shiro.SecurityUtils; |
import org.apache.shiro.authz.UnauthorizedException; |
|
/** |
* The Class FileInfoImpl. |
* @author Brian Rosenberger, bru(at)brutex.de |
* |
* @author Brian Rosenberger, bru(at)brutex.de |
*/ |
public class FileInfoImpl implements FileInfo { |
|
|
Logger logger = Logger.getLogger(FileInfoImpl.class); |
|
|
/* (non-Javadoc) |
* @see net.brutex.xservices.ws.rs.FileInfo#getFiles(javax.ws.rs.core.HttpHeaders, java.lang.String, boolean, boolean, int, java.lang.String, int, int) |
*/ |
public Response getFiles(HttpHeaders h, UriInfo uriInfo, String dir, boolean withDir, boolean withFiles, int level, String search, int count, int page, boolean useCache) |
public class FileInfoImpl implements FileInfo |
{ |
public Response getFiles(HttpHeaders h, String dir, boolean withDir, boolean withFiles, int level, String search, int count, int page) |
{ |
StandardSecurityManager sec = new StandardSecurityManager(); |
UserIdentity id = new UserIdentity(); |
|
isPermitted(dir); |
if (!sec.canExecute(java.lang.Thread.currentThread().getStackTrace()[1].getMethodName(), id)) { |
return null; |
} |
|
URI baseuri = URI.create(uriInfo.getBaseUri()+FileInfo.BASE_PATH+"getFile?file="); |
|
if(dir==null) {dir = "c:/"; System.out.println("No directory specified.");} |
logger.info(String.format("Listing directory '%s'.", dir)); |
System.out.println("Listing directory: " + dir); |
if (level <= 0) level = 1; |
|
if (level > 3) level = 3; |
if ((!withDir) && (!withFiles)) withFiles = true; |
String cachekey = level + "||" + withFiles + "||" + withDir + "||" + search + "||" + dir; |
try { |
logger.debug(String.format("Hitting cache with cachekey '%s'", cachekey)); |
JCS jcs = JCS.getInstance("FileCache"); |
|
/*Try to retrieve the file list from the cache*/ |
List<FileInfoType> list = (List<FileInfoType>)jcs.get(cachekey); |
|
if (list == null || !useCache) { |
list = setDirectory(baseuri, dir, withDir, withFiles, level, search); |
List list = (List)jcs.get(cachekey); |
if (list == null) { |
list = setDirectory(dir, withDir, withFiles, level, search); |
jcs.put(cachekey, list); |
logger.debug("Stored in Cache: " + list.toString()); |
System.out.println("Stored in Cache: " + list.toString()); |
} else { |
logger.debug("Got from Cache: " + list.toString()); |
System.out.println("Got from Cache: " + list.toString()); |
} |
|
int fromIndex = 0; |
105,8 → 52,9 |
toIndex = page * count; |
if (toIndex > list.size()) toIndex = list.size(); |
if (fromIndex > toIndex) fromIndex = toIndex; |
GenericEntity<List<FileInfoType>> sublist = new GenericEntity<List<FileInfoType>>(list.subList(fromIndex, toIndex)) {}; |
logger.info(String.format("Returning items %s to %s from total of %s items in the list.", fromIndex, toIndex, list.size())); |
GenericEntity sublist = new GenericEntity(list.subList(fromIndex, toIndex)) |
{ |
}; |
return Response.ok(sublist).build(); |
} catch (CacheException e) { |
Response.serverError().build(); |
114,138 → 62,34 |
return null; |
} |
|
/** |
* Sets the directory. |
* |
* @param list the list |
* @param dir the dir |
* @param withDirectories the with directories |
* @param withFiles the with files |
* @param depth the depth |
* @param search the search |
*/ |
private void setDirectory(final URI baseuri, final List<FileInfoType> list, File dir, boolean withDirectories, boolean withFiles, final int depth, String search) |
private void setDirectory(List<FileInfoType> list, File dir, boolean withDirectories, boolean withFiles, final int depth, final String search) |
{ |
if (depth <= 0) return; |
|
if(search==null || search.equals("") ) { |
search = "*"; |
logger.info("No search pattern supplied, using default '*'."); |
File[] files = dir.listFiles(new FileFilter() |
{ |
public boolean accept(File pathname) { |
if ((pathname.isDirectory()) && (depth > 1)) return true; |
if ((search == null) || (search.equals(""))) return true; |
if (!pathname.getAbsolutePath().contains(search)) return false; |
return true; |
} |
}); |
if ((dir.getParentFile() != null) && (withDirectories)) list.add(new FileInfoType(dir.getParentFile())); |
if (files == null) return; |
|
FileWalker finder = new FileWalker(search); |
try { |
Files.walkFileTree(dir.toPath(), EnumSet.of(FileVisitOption.FOLLOW_LINKS), depth, finder); |
logger.info("FileWalker returned '"+finder.getCount()+"' hits. '" + finder.getTotal() + "' files have been scanned."); |
List<Path> result = finder.getResult(); |
for(Path f : result) { |
list.add(new FileInfoType(f, baseuri)); |
for (File e : files) { |
if (e.isDirectory()) setDirectory(list, e, withDirectories, withFiles, depth - 1, search); |
if (((withDirectories) && (e.isDirectory())) || ( |
(withFiles) && (e.isFile()))) |
list.add(new FileInfoType(e)); |
} |
} catch (IOException e2) { |
logger.error(e2.getMessage(), e2);; |
} |
} |
|
/** |
* Sets the directory. |
* |
* @param dir the dir |
* @param withDirectories the with directories |
* @param withFiles the with files |
* @param depth the depth |
* @param search the search |
* @return the list |
*/ |
private List<FileInfoType> setDirectory(URI baseuri, String dir, boolean withDirectories, boolean withFiles, int depth, String search) |
private List<FileInfoType> setDirectory(String dir, boolean withDirectories, boolean withFiles, int depth, String search) |
{ |
List<FileInfoType> list = new ArrayList<FileInfoType>(); |
setDirectory(baseuri, list, new File(dir), withDirectories, withFiles, depth, search); |
List list = new ArrayList(); |
setDirectory(list, new File(dir), withDirectories, withFiles, depth, search); |
return list; |
} |
|
@Override |
public Response getFile(HttpHeaders paramHttpHeaders, String file) { |
isPermitted(file); |
try { |
Path path = FileSystems.getDefault().getPath(file); |
|
BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class); |
BasicFileAttributes basic; |
basic = basicView.readAttributes(); |
|
|
//In case this is a directory |
//we zip it and return the zip stream |
if(basic.isDirectory()) return getDirectoryAsZip(path); |
|
|
|
MediaType mime = MediaType.APPLICATION_OCTET_STREAM_TYPE; |
try { |
mime = MediaType.valueOf(Files.probeContentType(path)); |
} catch (IllegalArgumentException | IOException e) { |
//In case we can not find the media type for some reason |
//the default assignment is taken, so we can |
//ignore this error. |
logger.debug(String.format("Could not probe media type for file '%s'. Default is '%s'", path.toString(), mime.getType()), e); |
} |
Response r = Response.ok(path.toFile(), mime).build(); |
String fileName = path.getFileName().toString(); |
if(mime == MediaType.APPLICATION_OCTET_STREAM_TYPE) r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + fileName + "\""); |
return r; |
} catch (IOException e1) { |
// TODO Auto-generated catch block |
logger.error(e1.getMessage(), e1); |
return Response.serverError().build(); |
} |
} |
|
private Response getDirectoryAsZip(final Path path) { |
|
StreamingOutput output = new StreamingOutput() { |
|
@Override |
public void write(OutputStream os) throws IOException, |
WebApplicationException { |
ZipOutputStream zos = new ZipOutputStream(os); |
|
//read directory content (files only) |
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { |
for (Path file: stream) { |
//skip anything not being a file |
if(! file.toFile().isFile()) continue; |
|
//ZipEntry |
String filename = file.getFileName().toString(); |
ZipEntry ze = new ZipEntry(filename); |
zos.putNextEntry( ze ); |
|
//read a file and put it into the output stream |
FileInputStream fis = new FileInputStream(file.toFile()); |
byte[] buffer = new byte[1024]; |
int len; |
while ((len = fis.read(buffer)) > 0) { |
zos.write(buffer, 0, len); |
} |
zos.flush(); |
fis.close(); |
} |
zos.close(); |
} |
|
} |
}; |
Response r = Response.ok(output, MediaType.APPLICATION_OCTET_STREAM_TYPE).build(); |
String zipname = (path.getFileName()==null) ? "null.zip" : path.getFileName().toString()+".zip"; |
r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + zipname + "\""); |
return r; |
} |
|
private boolean isPermitted(String dir) { |
if(! SecurityUtils.getSubject().isPermitted( new DirectoryPermission(dir))) { |
logger.warn(String.format("User '%s' does not have permission to access '%s'.",SecurityUtils.getSubject().getPrincipal(), dir )); |
throw new NotAuthorizedException(new UnauthorizedException("User does not have permission to access "+ dir)); |
} |
return true; |
} |
} |