Subversion Repositories XServices

Rev

Rev 177 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 177 Rev 185
1
/*
1
/*
2
 *   Copyright 2013 Brian Rosenberger (Brutex Network)
2
 *   Copyright 2013 Brian Rosenberger (Brutex Network)
3
 *
3
 *
4
 *   Licensed under the Apache License, Version 2.0 (the "License");
4
 *   Licensed under the Apache License, Version 2.0 (the "License");
5
 *   you may not use this file except in compliance with the License.
5
 *   you may not use this file except in compliance with the License.
6
 *   You may obtain a copy of the License at
6
 *   You may obtain a copy of the License at
7
 *
7
 *
8
 *       http://www.apache.org/licenses/LICENSE-2.0
8
 *       http://www.apache.org/licenses/LICENSE-2.0
9
 *
9
 *
10
 *   Unless required by applicable law or agreed to in writing, software
10
 *   Unless required by applicable law or agreed to in writing, software
11
 *   distributed under the License is distributed on an "AS IS" BASIS,
11
 *   distributed under the License is distributed on an "AS IS" BASIS,
12
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *   See the License for the specific language governing permissions and
13
 *   See the License for the specific language governing permissions and
14
 *   limitations under the License.
14
 *   limitations under the License.
15
 */
15
 */
16
 
16
 
17
package net.brutex.xservices.ws.rs;
17
package net.brutex.xservices.ws.rs;
18
 
18
 
19
import java.io.File;
19
import java.io.File;
20
import java.io.FileInputStream;
20
import java.io.FileInputStream;
21
import java.io.FileOutputStream;
21
import java.io.FileOutputStream;
22
import java.io.IOException;
22
import java.io.IOException;
23
import java.io.OutputStream;
23
import java.io.OutputStream;
24
import java.net.URI;
24
import java.net.URI;
25
import java.nio.file.DirectoryStream;
25
import java.nio.file.DirectoryStream;
26
import java.nio.file.FileSystems;
26
import java.nio.file.FileSystems;
27
import java.nio.file.FileVisitOption;
27
import java.nio.file.FileVisitOption;
28
import java.nio.file.FileVisitResult;
28
import java.nio.file.FileVisitResult;
29
import java.nio.file.Files;
29
import java.nio.file.Files;
30
import java.nio.file.Path;
30
import java.nio.file.Path;
31
import java.nio.file.PathMatcher;
31
import java.nio.file.PathMatcher;
32
import java.nio.file.SimpleFileVisitor;
32
import java.nio.file.SimpleFileVisitor;
33
import java.nio.file.attribute.BasicFileAttributeView;
33
import java.nio.file.attribute.BasicFileAttributeView;
34
import java.nio.file.attribute.BasicFileAttributes;
34
import java.nio.file.attribute.BasicFileAttributes;
35
import java.util.ArrayList;
35
import java.util.ArrayList;
36
import java.util.EnumSet;
36
import java.util.EnumSet;
37
import java.util.List;
37
import java.util.List;
38
import java.util.zip.ZipEntry;
38
import java.util.zip.ZipEntry;
39
import java.util.zip.ZipOutputStream;
39
import java.util.zip.ZipOutputStream;
40
 
40
 
41
import javax.ws.rs.NotAuthorizedException;
41
import javax.ws.rs.NotAuthorizedException;
42
import javax.ws.rs.WebApplicationException;
42
import javax.ws.rs.WebApplicationException;
43
import javax.ws.rs.core.GenericEntity;
43
import javax.ws.rs.core.GenericEntity;
44
import javax.ws.rs.core.HttpHeaders;
44
import javax.ws.rs.core.HttpHeaders;
45
import javax.ws.rs.core.MediaType;
45
import javax.ws.rs.core.MediaType;
46
import javax.ws.rs.core.Response;
46
import javax.ws.rs.core.Response;
47
import javax.ws.rs.core.StreamingOutput;
47
import javax.ws.rs.core.StreamingOutput;
48
import javax.ws.rs.core.UriInfo;
48
import javax.ws.rs.core.UriInfo;
49
 
49
 
50
import net.brutex.xservices.security.DirectoryPermission;
50
import net.brutex.xservices.security.DirectoryPermission;
51
import net.brutex.xservices.types.FileInfoType;
51
import net.brutex.xservices.types.FileInfoType;
52
import net.brutex.xservices.util.FileWalker;
52
import net.brutex.xservices.util.FileWalker;
53
 
53
 
-
 
54
import org.apache.commons.jcs.JCS;
54
import org.apache.jcs.JCS;
55
import org.apache.commons.jcs.access.CacheAccess;
-
 
56
import org.apache.commons.jcs.access.exception.CacheException;
55
import org.apache.jcs.access.exception.CacheException;
57
import org.apache.logging.log4j.LogManager;
56
import org.apache.log4j.Logger;
58
import org.apache.logging.log4j.Logger;
57
import org.apache.shiro.SecurityUtils;
59
import org.apache.shiro.SecurityUtils;
58
import org.apache.shiro.authz.UnauthorizedException;
60
import org.apache.shiro.authz.UnauthorizedException;
59
 
61
 
60
/**
62
/**
61
 * The Class FileInfoImpl.
63
 * The Class FileInfoImpl.
62
 *
64
 *
63
 * @author Brian Rosenberger, bru(at)brutex.de
65
 * @author Brian Rosenberger, bru(at)brutex.de
64
 */
66
 */
65
public class FileInfoImpl  implements FileInfo {
67
public class FileInfoImpl  implements FileInfo {
66
	
68
	
67
	
69
	
68
	Logger logger = Logger.getLogger(FileInfoImpl.class);
70
	Logger logger = LogManager.getLogger();
69
	
71
	
70
 
72
 
71
  /* (non-Javadoc)
73
  /* (non-Javadoc)
72
   * @see net.brutex.xservices.ws.rs.FileInfo#getFiles(javax.ws.rs.core.HttpHeaders, java.lang.String, boolean, boolean, int, java.lang.String, int, int)
74
   * @see net.brutex.xservices.ws.rs.FileInfo#getFiles(javax.ws.rs.core.HttpHeaders, java.lang.String, boolean, boolean, int, java.lang.String, int, int)
73
   */
75
   */
74
  public Response getFiles(HttpHeaders h, UriInfo uriInfo, String dir, boolean withDir, boolean withFiles, int level, String search, int count, int page, boolean useCache)
76
  public Response getFiles(HttpHeaders h, UriInfo uriInfo, String dir, boolean withDir, boolean withFiles, int level, String search, int count, int page, boolean useCache)
75
  {
77
  {
76
	if(dir==null) {
78
	if(dir==null) {
77
		dir = "c:/"; 
79
		dir = "c:/"; 
78
		logger.warn("No directory specified. Default is 'c:/'.");
80
		logger.warn("No directory specified. Default is 'c:/'.");
79
		}
81
		}
80
	isPermitted(dir);
82
	isPermitted(dir);
81
	
83
	
82
    URI baseuri = URI.create(uriInfo.getBaseUri()+FileInfo.BASE_PATH+"getFile?file=");
84
    URI baseuri = URI.create(uriInfo.getBaseUri()+FileInfo.BASE_PATH+"getFile?file=");
83
    
85
    
84
    logger.info(String.format("Listing directory '%s'.", dir));
86
    logger.info(String.format("Listing directory '%s'.", dir));
85
    if (level <= 0) level = 1;
87
    if (level <= 0) level = 1;
86
 
88
 
87
    if ((!withDir) && (!withFiles)) withFiles = true;
89
    if ((!withDir) && (!withFiles)) withFiles = true;
88
    String cachekey = level + "||" + withFiles + "||" + withDir + "||" + search + "||" + dir;
90
    String cachekey = level + "||" + withFiles + "||" + withDir + "||" + search + "||" + dir;
89
    try {
91
    try {
90
      logger.debug(String.format("Hitting cache with cachekey '%s'", cachekey));
92
      logger.debug(String.format("Hitting cache with cachekey '%s'", cachekey));
91
      JCS jcs = JCS.getInstance("FileCache");
93
      CacheAccess<Object, Object> jcs = JCS.getInstance("FileCache");
92
 
94
 
93
      /*Try to retrieve the file list from the cache*/
95
      /*Try to retrieve the file list from the cache*/
94
      List<FileInfoType> list = (List<FileInfoType>)jcs.get(cachekey);
96
      List<FileInfoType> list = (List<FileInfoType>)jcs.get(cachekey);
95
      
97
      
96
      if (list == null || !useCache) {
98
      if (list == null || !useCache) {
97
        list = setDirectory(baseuri, dir, withDir, withFiles, level, search);
99
        list = setDirectory(baseuri, dir, withDir, withFiles, level, search);
98
        jcs.put(cachekey, list);
100
        jcs.put(cachekey, list);
99
        logger.debug("Stored in Cache: " + list.toString());
101
        logger.debug("Stored in Cache: " + list.toString());
100
      } else {
102
      } else {
101
        logger.debug("Got from Cache: " + list.toString());
103
        logger.debug("Got from Cache: " + list.toString());
102
      }
104
      }
103
 
105
 
104
      int fromIndex = 0;
106
      int fromIndex = 0;
105
      int toIndex = 0;
107
      int toIndex = 0;
106
      fromIndex = (page - 1) * count;
108
      fromIndex = (page - 1) * count;
107
      toIndex = page * count;
109
      toIndex = page * count;
108
      if (toIndex > list.size()) toIndex = list.size();
110
      if (toIndex > list.size()) toIndex = list.size();
109
      if (fromIndex > toIndex) fromIndex = toIndex;
111
      if (fromIndex > toIndex) fromIndex = toIndex;
110
      GenericEntity<List<FileInfoType>> sublist = new GenericEntity<List<FileInfoType>>(list.subList(fromIndex, toIndex)) {};
112
      GenericEntity<List<FileInfoType>> sublist = new GenericEntity<List<FileInfoType>>(list.subList(fromIndex, toIndex)) {};
111
      logger.info(String.format("Returning items %s to %s from total of %s items in the list.", fromIndex, toIndex, list.size()));
113
      logger.info(String.format("Returning items %s to %s from total of %s items in the list.", fromIndex, toIndex, list.size()));
112
      return Response.ok(sublist).build();
114
      return Response.ok(sublist).build();
113
    } catch (CacheException e) {
115
    } catch (CacheException e) {
114
      return Response.serverError().build();
116
      return Response.serverError().build();
115
    }
117
    }
116
  }
118
  }
117
 
119
 
118
  /**
120
  /**
119
   * Sets the directory.
121
   * Sets the directory.
120
   *
122
   *
121
   * @param list the list
123
   * @param list the list
122
   * @param dir the dir
124
   * @param dir the dir
123
   * @param withDirectories the with directories
125
   * @param withDirectories the with directories
124
   * @param withFiles the with files
126
   * @param withFiles the with files
125
   * @param depth the depth
127
   * @param depth the depth
126
   * @param search the search
128
   * @param search the search
127
   */
129
   */
128
  private void setDirectory(final URI baseuri, final List<FileInfoType> list, File dir, boolean withDirectories, boolean withFiles, final int depth, String search)
130
  private void setDirectory(final URI baseuri, final List<FileInfoType> list, File dir, boolean withDirectories, boolean withFiles, final int depth, String search)
129
  {
131
  {
130
    if (depth <= 0) return;
132
    if (depth <= 0) return;
131
    
133
    
132
    	if(search==null || search.equals("") ) {
134
    	if(search==null || search.equals("") ) {
133
    		search = "*";
135
    		search = "*";
134
    		logger.info("No search pattern supplied, using default '*'.");
136
    		logger.info("No search pattern supplied, using default '*'.");
135
    	}
137
    	}
136
    	
138
    	
137
    	FileWalker finder = new FileWalker(search);
139
    	FileWalker finder = new FileWalker(search);
138
    	try {
140
    	try {
139
			Files.walkFileTree(dir.toPath(), EnumSet.of(FileVisitOption.FOLLOW_LINKS), depth, finder);
141
			Files.walkFileTree(dir.toPath(), EnumSet.of(FileVisitOption.FOLLOW_LINKS), depth, finder);
140
			logger.info("FileWalker returned '"+finder.getCount()+"' hits. '" + finder.getTotal() + "' files have been scanned.");
142
			logger.info("FileWalker returned '"+finder.getCount()+"' hits. '" + finder.getTotal() + "' files have been scanned.");
141
			List<Path> result = finder.getResult();
143
			List<Path> result = finder.getResult();
142
	    	for(Path f : result) {
144
	    	for(Path f : result) {
143
	    		if(! withDirectories) {
145
	    		if(! withDirectories) {
144
	    			if(f.toFile().isDirectory()) continue;
146
	    			if(f.toFile().isDirectory()) continue;
145
	    		}
147
	    		}
146
	    		if(! withFiles) {
148
	    		if(! withFiles) {
147
	    			if(f.toFile().isFile()) continue;
149
	    			if(f.toFile().isFile()) continue;
148
	    		}
150
	    		}
149
	    		list.add(new FileInfoType(f, baseuri));
151
	    		list.add(new FileInfoType(f, baseuri));
150
	    	}
152
	    	}
151
		} catch (IOException e2) {
153
		} catch (IOException e2) {
152
			logger.error(e2.getMessage(), e2);;
154
			logger.error(e2.getMessage(), e2);;
153
		}
155
		}
154
  }
156
  }
155
  
157
  
156
  /**
158
  /**
157
   * Sets the directory.
159
   * Sets the directory.
158
   *
160
   *
159
   * @param dir the dir
161
   * @param dir the dir
160
   * @param withDirectories the with directories
162
   * @param withDirectories the with directories
161
   * @param withFiles the with files
163
   * @param withFiles the with files
162
   * @param depth the depth
164
   * @param depth the depth
163
   * @param search the search
165
   * @param search the search
164
   * @return the list
166
   * @return the list
165
   */
167
   */
166
  private List<FileInfoType> setDirectory(URI baseuri, String dir, boolean withDirectories, boolean withFiles, int depth, String search)
168
  private List<FileInfoType> setDirectory(URI baseuri, String dir, boolean withDirectories, boolean withFiles, int depth, String search)
167
  {
169
  {
168
    List<FileInfoType> list = new ArrayList<FileInfoType>();
170
    List<FileInfoType> list = new ArrayList<FileInfoType>();
169
    setDirectory(baseuri, list, new File(dir), withDirectories, withFiles, depth, search);
171
    setDirectory(baseuri, list, new File(dir), withDirectories, withFiles, depth, search);
170
    return list;
172
    return list;
171
  }
173
  }
172
 
174
 
173
@Override
175
@Override
174
public Response getFile(HttpHeaders paramHttpHeaders, String file) {
176
public Response getFile(HttpHeaders paramHttpHeaders, String file) {
175
	isPermitted(file);
177
	isPermitted(file);
176
	try {
178
	try {
177
	Path path = FileSystems.getDefault().getPath(file);
179
	Path path = FileSystems.getDefault().getPath(file);
178
	
180
	
179
	BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
181
	BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
180
	BasicFileAttributes basic;
182
	BasicFileAttributes basic;
181
	basic = basicView.readAttributes();
183
	basic = basicView.readAttributes();
182
	
184
	
183
	
185
	
184
	//In case this is a directory
186
	//In case this is a directory
185
	//we zip it and return the zip stream
187
	//we zip it and return the zip stream
186
	if(basic.isDirectory()) return getDirectoryAsZip(path);
188
	if(basic.isDirectory()) return getDirectoryAsZip(path);
187
	
189
	
188
	
190
	
189
	
191
	
190
	MediaType mime = MediaType.APPLICATION_OCTET_STREAM_TYPE;
192
	MediaType mime = MediaType.APPLICATION_OCTET_STREAM_TYPE;
191
	try {
193
	try {
192
		mime = MediaType.valueOf(Files.probeContentType(path));
194
		mime = MediaType.valueOf(Files.probeContentType(path));
193
	} catch (IllegalArgumentException | IOException e) {
195
	} catch (IllegalArgumentException | IOException e) {
194
		//In case we can not find the media type for some reason
196
		//In case we can not find the media type for some reason
195
		//the default assignment is taken, so we can
197
		//the default assignment is taken, so we can
196
		//ignore this error.
198
		//ignore this error.
197
		logger.debug(String.format("Could not probe media type for file '%s'. Default is '%s'", path.toString(), mime.getType()), e);
199
		logger.debug(String.format("Could not probe media type for file '%s'. Default is '%s'", path.toString(), mime.getType()), e);
198
	}
200
	}
199
	Response r = Response.ok(path.toFile(), mime).build();
201
	Response r = Response.ok(path.toFile(), mime).build();
200
	String fileName = path.getFileName().toString();
202
	String fileName = path.getFileName().toString();
201
	if(mime == MediaType.APPLICATION_OCTET_STREAM_TYPE) r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
203
	if(mime == MediaType.APPLICATION_OCTET_STREAM_TYPE) r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
202
	return r;
204
	return r;
203
		} catch (IOException e1) {
205
		} catch (IOException e1) {
204
			// TODO Auto-generated catch block
206
			// TODO Auto-generated catch block
205
			logger.error(e1.getMessage(), e1);
207
			logger.error(e1.getMessage(), e1);
206
			return Response.serverError().build();
208
			return Response.serverError().build();
207
		}
209
		}
208
}
210
}
209
 
211
 
210
private Response getDirectoryAsZip(final Path path) {
212
private Response getDirectoryAsZip(final Path path) {
211
 
213
 
212
	StreamingOutput output = new StreamingOutput() {
214
	StreamingOutput output = new StreamingOutput() {
213
		
215
		
214
		@Override
216
		@Override
215
		public void write(OutputStream os) throws IOException,
217
		public void write(OutputStream os) throws IOException,
216
				WebApplicationException {
218
				WebApplicationException {
217
			ZipOutputStream zos = new ZipOutputStream(os);
219
			ZipOutputStream zos = new ZipOutputStream(os);
218
			
220
			
219
			//read directory content (files only)
221
			//read directory content (files only)
220
			try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
222
			try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
221
			    for (Path file: stream) {
223
			    for (Path file: stream) {
222
			    	//skip anything not being a file
224
			    	//skip anything not being a file
223
			        if(! file.toFile().isFile()) continue;
225
			        if(! file.toFile().isFile()) continue;
224
			        
226
			        
225
			        //ZipEntry
227
			        //ZipEntry
226
			        String filename = file.getFileName().toString();
228
			        String filename = file.getFileName().toString();
227
			        ZipEntry ze = new ZipEntry(filename);
229
			        ZipEntry ze = new ZipEntry(filename);
228
			        zos.putNextEntry( ze );
230
			        zos.putNextEntry( ze );
229
			        
231
			        
230
			        //read a file and put it into the output stream
232
			        //read a file and put it into the output stream
231
			        FileInputStream fis = new FileInputStream(file.toFile());
233
			        FileInputStream fis = new FileInputStream(file.toFile());
232
			        byte[] buffer = new byte[1024];
234
			        byte[] buffer = new byte[1024];
233
			        int len;
235
			        int len;
234
			        while ((len = fis.read(buffer)) > 0) {
236
			        while ((len = fis.read(buffer)) > 0) {
235
			        	zos.write(buffer, 0, len);
237
			        	zos.write(buffer, 0, len);
236
			        }
238
			        }
237
			        zos.flush();
239
			        zos.flush();
238
			        fis.close();
240
			        fis.close();
239
			    }
241
			    }
240
			    zos.close();
242
			    zos.close();
241
			}
243
			}
242
			
244
			
243
		}
245
		}
244
	};
246
	};
245
	Response r = Response.ok(output, MediaType.APPLICATION_OCTET_STREAM_TYPE).build();
247
	Response r = Response.ok(output, MediaType.APPLICATION_OCTET_STREAM_TYPE).build();
246
	String zipname = (path.getFileName()==null) ? "null.zip" : path.getFileName().toString()+".zip";
248
	String zipname = (path.getFileName()==null) ? "null.zip" : path.getFileName().toString()+".zip";
247
	r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + zipname + "\"");
249
	r.getHeaders().add("Content-Disposition", "attachment; filename=\"" + zipname + "\"");
248
	return r;
250
	return r;
249
}
251
}
250
 
252
 
251
private boolean isPermitted(String dir) {
253
private boolean isPermitted(String dir) {
252
	if(! SecurityUtils.getSubject().isPermitted( new DirectoryPermission(dir))) {
254
	if(! SecurityUtils.getSubject().isPermitted( new DirectoryPermission(dir))) {
253
		logger.warn(String.format("User '%s' does not have permission to access '%s'.",SecurityUtils.getSubject().getPrincipal(), dir ));
255
		logger.warn(String.format("User '%s' does not have permission to access '%s'.",SecurityUtils.getSubject().getPrincipal(), dir ));
254
		throw new NotAuthorizedException(new UnauthorizedException("User does not have permission to access "+ dir));
256
		throw new NotAuthorizedException(new UnauthorizedException("User does not have permission to access "+ dir));
255
	}
257
	}
256
	return true;
258
	return true;
257
}
259
}
258
 
260
 
259
//http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
261
//http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
260
private static String humanReadableByteCount(long bytes, boolean si) {
262
private static String humanReadableByteCount(long bytes, boolean si) {
261
    int unit = si ? 1000 : 1024;
263
    int unit = si ? 1000 : 1024;
262
    if (bytes < unit) return bytes + " B";
264
    if (bytes < unit) return bytes + " B";
263
    int exp = (int) (Math.log(bytes) / Math.log(unit));
265
    int exp = (int) (Math.log(bytes) / Math.log(unit));
264
    String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
266
    String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
265
    return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
267
    return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
266
}
268
}
267
 
269
 
268
}
270
}
269
 
271
 
270
Generated by GNU Enscript 1.6.5.90.
272
Generated by GNU Enscript 1.6.5.90.
271
 
273
 
272
 
274