1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mortbay.servlet;
17
18 import java.io.File;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.net.URI;
24 import java.net.URISyntaxException;
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29
30 import javax.servlet.Filter;
31 import javax.servlet.FilterChain;
32 import javax.servlet.FilterConfig;
33 import javax.servlet.ServletContext;
34 import javax.servlet.ServletException;
35 import javax.servlet.ServletRequest;
36 import javax.servlet.ServletResponse;
37 import javax.servlet.UnavailableException;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40
41 import org.mortbay.util.IO;
42 import org.mortbay.util.URIUtil;
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class PutFilter implements Filter
57 {
58 public final static String __PUT="PUT";
59 public final static String __DELETE="DELETE";
60 public final static String __MOVE="MOVE";
61 public final static String __OPTIONS="OPTIONS";
62
63 Set _operations = new HashSet();
64 protected ConcurrentMap _hidden = new ConcurrentHashMap();
65
66 protected ServletContext _context;
67 protected String _baseURI;
68 protected boolean _delAllowed;
69
70
71 public void init(FilterConfig config) throws ServletException
72 {
73 _context=config.getServletContext();
74 if (_context.getRealPath("/")==null)
75 throw new UnavailableException("Packed war");
76
77 String b = config.getInitParameter("baseURI");
78 if (b != null)
79 {
80 _baseURI=b;
81 }
82 else
83 {
84 File base=new File(_context.getRealPath("/"));
85 _baseURI=base.toURI().toString();
86 }
87
88 _delAllowed = getInitBoolean(config,"delAllowed");
89
90 _operations.add(__OPTIONS);
91 _operations.add(__PUT);
92 if (_delAllowed)
93 {
94 _operations.add(__DELETE);
95 _operations.add(__MOVE);
96 }
97 }
98
99
100 private boolean getInitBoolean(FilterConfig config,String name)
101 {
102 String value = config.getInitParameter(name);
103 return value != null && value.length() > 0 && (value.startsWith("t") || value.startsWith("T") || value.startsWith("y") || value.startsWith("Y") || value.startsWith("1"));
104 }
105
106
107 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
108 {
109
110 HttpServletRequest request=(HttpServletRequest)req;
111 HttpServletResponse response=(HttpServletResponse)res;
112
113 String servletPath =request.getServletPath();
114 String pathInfo = request.getPathInfo();
115 String pathInContext = URIUtil.addPaths(servletPath, pathInfo);
116
117 String resource = URIUtil.addPaths(_baseURI,pathInContext);
118
119 String method = request.getMethod();
120 boolean op = _operations.contains(method);
121
122 if (op)
123 {
124 File file = null;
125 try
126 {
127 if (method.equals(__OPTIONS))
128 handleOptions(request, response);
129 else
130 {
131 file=new File(new URI(resource));
132 boolean exists = file.exists();
133 if (exists && !passConditionalHeaders(request, response, file))
134 return;
135
136 if (method.equals(__PUT))
137 handlePut(request, response,pathInContext, file);
138 else if (method.equals(__DELETE))
139 handleDelete(request, response, pathInContext, file);
140 else if (method.equals(__MOVE))
141 handleMove(request, response, pathInContext, file);
142 else
143 throw new IllegalStateException();
144 }
145 }
146 catch(Exception e)
147 {
148 _context.log(e.toString(),e);
149 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
150 }
151 }
152 else
153 {
154 if (isHidden(pathInContext))
155 response.sendError(HttpServletResponse.SC_NOT_FOUND);
156 else
157 chain.doFilter(request,response);
158 return;
159 }
160 }
161
162
163 private boolean isHidden(String pathInContext)
164 {
165 return _hidden.containsKey(pathInContext);
166 }
167
168
169 public void destroy()
170 {
171 }
172
173
174 public void handlePut(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file) throws ServletException, IOException
175 {
176 boolean exists = file.exists();
177 if (pathInContext.endsWith("/"))
178 {
179 if (!exists)
180 {
181 if (!file.mkdirs())
182 response.sendError(HttpServletResponse.SC_FORBIDDEN);
183 else
184 {
185 response.setStatus(HttpServletResponse.SC_CREATED);
186 response.flushBuffer();
187 }
188 }
189 else
190 {
191 response.setStatus(HttpServletResponse.SC_OK);
192 response.flushBuffer();
193 }
194 }
195 else
196 {
197 boolean ok=false;
198 try
199 {
200 _hidden.put(pathInContext,pathInContext);
201 File parent = file.getParentFile();
202 parent.mkdirs();
203 int toRead = request.getContentLength();
204 InputStream in = request.getInputStream();
205 OutputStream out = new FileOutputStream(file,false);
206 if (toRead >= 0)
207 IO.copy(in, out, toRead);
208 else
209 IO.copy(in, out);
210 out.close();
211
212 response.setStatus(exists ? HttpServletResponse.SC_OK : HttpServletResponse.SC_CREATED);
213 response.flushBuffer();
214 ok=true;
215 }
216 catch (Exception ex)
217 {
218 _context.log(ex.toString(),ex);
219 response.sendError(HttpServletResponse.SC_FORBIDDEN);
220 }
221 finally
222 {
223 if (!ok)
224 {
225 try
226 {
227 if (file.exists())
228 file.delete();
229 }
230 catch(Exception e)
231 {
232 _context.log(e.toString(),e);
233 }
234 }
235 _hidden.remove(pathInContext);
236 }
237 }
238 }
239
240
241 public void handleDelete(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file) throws ServletException, IOException
242 {
243 try
244 {
245
246 if (file.delete())
247 {
248 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
249 response.flushBuffer();
250 }
251 else
252 response.sendError(HttpServletResponse.SC_FORBIDDEN);
253 }
254 catch (SecurityException sex)
255 {
256 _context.log(sex.toString(),sex);
257 response.sendError(HttpServletResponse.SC_FORBIDDEN);
258 }
259 }
260
261
262 public void handleMove(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file)
263 throws ServletException, IOException, URISyntaxException
264 {
265 String newPath = URIUtil.canonicalPath(request.getHeader("new-uri"));
266 if (newPath == null)
267 {
268 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
269 return;
270 }
271
272 String contextPath = request.getContextPath();
273 if (contextPath != null && !newPath.startsWith(contextPath))
274 {
275 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
276 return;
277 }
278 String newInfo = newPath;
279 if (contextPath != null)
280 newInfo = newInfo.substring(contextPath.length());
281
282 String new_resource = URIUtil.addPaths(_baseURI,newInfo);
283 File new_file=new File(new URI(new_resource));
284
285 file.renameTo(new_file);
286
287 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
288 response.flushBuffer();
289
290
291 }
292
293
294 public void handleOptions(HttpServletRequest request, HttpServletResponse response) throws IOException
295 {
296
297 throw new UnsupportedOperationException("Not Implemented");
298 }
299
300
301
302
303
304 protected boolean passConditionalHeaders(HttpServletRequest request, HttpServletResponse response, File file) throws IOException
305 {
306 long date = 0;
307
308 if ((date = request.getDateHeader("if-unmodified-since")) > 0)
309 {
310 if (file.lastModified() / 1000 > date / 1000)
311 {
312 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
313 return false;
314 }
315 }
316
317 if ((date = request.getDateHeader("if-modified-since")) > 0)
318 {
319 if (file.lastModified() / 1000 <= date / 1000)
320 {
321 response.reset();
322 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
323 response.flushBuffer();
324 return false;
325 }
326 }
327 return true;
328 }
329 }