Subversion Repositories XServices

Rev

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

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