Subversion Repositories XServices

Rev

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

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