Subversion Repositories XServices

Rev

Rev 198 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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