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<String> _operations = new HashSet<String>();
64 protected ConcurrentMap<String,String> _hidden = new ConcurrentHashMap<String, String>();
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 System.err.println("Doing PUT filter "+resource);
119
120 String method = request.getMethod();
121 boolean op = _operations.contains(method);
122
123 if (op)
124 {
125 File file = null;
126 try
127 {
128 if (method.equals(__OPTIONS))
129 handleOptions(request, response);
130 else
131 {
132 file=new File(new URI(resource));
133 boolean exists = file.exists();
134 if (exists && !passConditionalHeaders(request, response, file))
135 return;
136
137 if (method.equals(__PUT))
138 handlePut(request, response,pathInContext, file);
139 else if (method.equals(__DELETE))
140 handleDelete(request, response, pathInContext, file);
141 else if (method.equals(__MOVE))
142 handleMove(request, response, pathInContext, file);
143 else
144 throw new IllegalStateException();
145 }
146 }
147 catch(Exception e)
148 {
149 _context.log(e.toString(),e);
150 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
151 }
152 }
153 else
154 {
155 if (isHidden(pathInContext))
156 response.sendError(HttpServletResponse.SC_NOT_FOUND);
157 else
158 chain.doFilter(request,response);
159 return;
160 }
161 }
162
163
164 private boolean isHidden(String pathInContext)
165 {
166 return _hidden.containsKey(pathInContext);
167 }
168
169
170 public void destroy()
171 {
172 }
173
174
175 public void handlePut(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file) throws ServletException, IOException
176 {
177 boolean exists = file.exists();
178 if (pathInContext.endsWith("/"))
179 {
180 if (!exists)
181 {
182 if (!file.mkdirs())
183 response.sendError(HttpServletResponse.SC_FORBIDDEN);
184 else
185 {
186 response.setStatus(HttpServletResponse.SC_CREATED);
187 response.flushBuffer();
188 }
189 }
190 else
191 {
192 response.setStatus(HttpServletResponse.SC_OK);
193 response.flushBuffer();
194 }
195 }
196 else
197 {
198 boolean ok=false;
199 try
200 {
201 _hidden.put(pathInContext,pathInContext);
202 File parent = file.getParentFile();
203 parent.mkdirs();
204 int toRead = request.getContentLength();
205 InputStream in = request.getInputStream();
206 OutputStream out = new FileOutputStream(file,false);
207 if (toRead >= 0)
208 IO.copy(in, out, toRead);
209 else
210 IO.copy(in, out);
211 out.close();
212
213 response.setStatus(exists ? HttpServletResponse.SC_OK : HttpServletResponse.SC_CREATED);
214 response.flushBuffer();
215 ok=true;
216 }
217 catch (Exception ex)
218 {
219 _context.log(ex.toString(),ex);
220 response.sendError(HttpServletResponse.SC_FORBIDDEN);
221 }
222 finally
223 {
224 if (!ok)
225 {
226 try
227 {
228 if (file.exists())
229 file.delete();
230 }
231 catch(Exception e)
232 {
233 _context.log(e.toString(),e);
234 }
235 }
236 _hidden.remove(pathInContext);
237 }
238 }
239 }
240
241
242 public void handleDelete(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file) throws ServletException, IOException
243 {
244 try
245 {
246
247 if (file.delete())
248 {
249 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
250 response.flushBuffer();
251 }
252 else
253 response.sendError(HttpServletResponse.SC_FORBIDDEN);
254 }
255 catch (SecurityException sex)
256 {
257 _context.log(sex.toString(),sex);
258 response.sendError(HttpServletResponse.SC_FORBIDDEN);
259 }
260 }
261
262
263 public void handleMove(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file)
264 throws ServletException, IOException, URISyntaxException
265 {
266 String newPath = URIUtil.canonicalPath(request.getHeader("new-uri"));
267 if (newPath == null)
268 {
269 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
270 return;
271 }
272
273 String contextPath = request.getContextPath();
274 if (contextPath != null && !newPath.startsWith(contextPath))
275 {
276 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
277 return;
278 }
279 String newInfo = newPath;
280 if (contextPath != null)
281 newInfo = newInfo.substring(contextPath.length());
282
283 String new_resource = URIUtil.addPaths(_baseURI,newInfo);
284 File new_file=new File(new URI(new_resource));
285
286 file.renameTo(new_file);
287
288 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
289 response.flushBuffer();
290
291
292 }
293
294
295 public void handleOptions(HttpServletRequest request, HttpServletResponse response) throws IOException
296 {
297
298 throw new UnsupportedOperationException("Not Implemented");
299 }
300
301
302
303
304
305 protected boolean passConditionalHeaders(HttpServletRequest request, HttpServletResponse response, File file) throws IOException
306 {
307 long date = 0;
308
309 if ((date = request.getDateHeader("if-unmodified-since")) > 0)
310 {
311 if (file.lastModified() / 1000 > date / 1000)
312 {
313 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
314 return false;
315 }
316 }
317
318 if ((date = request.getDateHeader("if-modified-since")) > 0)
319 {
320 if (file.lastModified() / 1000 <= date / 1000)
321 {
322 response.reset();
323 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
324 response.flushBuffer();
325 return false;
326 }
327 }
328 return true;
329 }
330 }