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