root / src / gr / ebs / gss / server / rest / Webdav.java @ af6aa461
History | View | Annotate | Download (112.6 kB)
1 | 14ad7326 | pastith | /*
|
---|---|---|---|
2 | 14ad7326 | pastith | * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd.
|
3 | 14ad7326 | pastith | *
|
4 | 14ad7326 | pastith | * This file is part of GSS.
|
5 | 14ad7326 | pastith | *
|
6 | 14ad7326 | pastith | * GSS is free software: you can redistribute it and/or modify
|
7 | 14ad7326 | pastith | * it under the terms of the GNU General Public License as published by
|
8 | 14ad7326 | pastith | * the Free Software Foundation, either version 3 of the License, or
|
9 | 14ad7326 | pastith | * (at your option) any later version.
|
10 | 14ad7326 | pastith | *
|
11 | 14ad7326 | pastith | * GSS is distributed in the hope that it will be useful,
|
12 | 14ad7326 | pastith | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 14ad7326 | pastith | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 | 14ad7326 | pastith | * GNU General Public License for more details.
|
15 | 14ad7326 | pastith | *
|
16 | 14ad7326 | pastith | * You should have received a copy of the GNU General Public License
|
17 | 14ad7326 | pastith | * along with GSS. If not, see <http://www.gnu.org/licenses/>.
|
18 | 14ad7326 | pastith | */
|
19 | af6aa461 | Christos V. Stathis | package gr.ebs.gss.server.rest; |
20 | 14ad7326 | pastith | |
21 | bde4eafb | pastith | import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration; |
22 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.common.exceptions.DuplicateNameException; |
23 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.common.exceptions.GSSIOException; |
24 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.common.exceptions.InsufficientPermissionsException; |
25 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.common.exceptions.ObjectNotFoundException; |
26 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.common.exceptions.QuotaExceededException; |
27 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.common.exceptions.RpcException; |
28 | f7c44c33 | fstamatelopoulos | import gr.ebs.gss.server.domain.AuditInfo; |
29 | f7c44c33 | fstamatelopoulos | import gr.ebs.gss.server.domain.FileBody; |
30 | f7c44c33 | fstamatelopoulos | import gr.ebs.gss.server.domain.FileHeader; |
31 | f7c44c33 | fstamatelopoulos | import gr.ebs.gss.server.domain.Folder; |
32 | 14ad7326 | pastith | import gr.ebs.gss.server.domain.User; |
33 | 14ad7326 | pastith | import gr.ebs.gss.server.ejb.ExternalAPI; |
34 | 3b6b7f25 | Dimitris Routsis | import gr.ebs.gss.server.ejb.TransactionHelper; |
35 | 14ad7326 | pastith | |
36 | 14ad7326 | pastith | import java.io.BufferedInputStream; |
37 | 14ad7326 | pastith | import java.io.ByteArrayInputStream; |
38 | 14ad7326 | pastith | import java.io.ByteArrayOutputStream; |
39 | 14ad7326 | pastith | import java.io.File; |
40 | 14ad7326 | pastith | import java.io.FileInputStream; |
41 | 14ad7326 | pastith | import java.io.IOException; |
42 | 14ad7326 | pastith | import java.io.InputStream; |
43 | 14ad7326 | pastith | import java.io.InputStreamReader; |
44 | 14ad7326 | pastith | import java.io.OutputStreamWriter; |
45 | 14ad7326 | pastith | import java.io.PrintWriter; |
46 | 14ad7326 | pastith | import java.io.RandomAccessFile; |
47 | 14ad7326 | pastith | import java.io.Reader; |
48 | 14ad7326 | pastith | import java.io.StringReader; |
49 | 14ad7326 | pastith | import java.io.StringWriter; |
50 | b36b0a98 | droutsis | import java.io.UnsupportedEncodingException; |
51 | 14ad7326 | pastith | import java.io.Writer; |
52 | b36b0a98 | droutsis | import java.net.URLDecoder; |
53 | 14ad7326 | pastith | import java.security.MessageDigest; |
54 | 14ad7326 | pastith | import java.security.NoSuchAlgorithmException; |
55 | 14ad7326 | pastith | import java.text.SimpleDateFormat; |
56 | 14ad7326 | pastith | import java.util.ArrayList; |
57 | 14ad7326 | pastith | import java.util.Date; |
58 | 14ad7326 | pastith | import java.util.Enumeration; |
59 | 14ad7326 | pastith | import java.util.Hashtable; |
60 | 14ad7326 | pastith | import java.util.Iterator; |
61 | 14ad7326 | pastith | import java.util.List; |
62 | 14ad7326 | pastith | import java.util.Locale; |
63 | 14ad7326 | pastith | import java.util.Stack; |
64 | 14ad7326 | pastith | import java.util.StringTokenizer; |
65 | 14ad7326 | pastith | import java.util.TimeZone; |
66 | 14ad7326 | pastith | import java.util.Vector; |
67 | 2f1a60e0 | Dimitris Routsis | import java.util.concurrent.Callable; |
68 | 14ad7326 | pastith | |
69 | 14ad7326 | pastith | import javax.naming.Context; |
70 | 14ad7326 | pastith | import javax.naming.InitialContext; |
71 | 14ad7326 | pastith | import javax.naming.NamingException; |
72 | 14ad7326 | pastith | import javax.rmi.PortableRemoteObject; |
73 | 14ad7326 | pastith | import javax.servlet.ServletContext; |
74 | 14ad7326 | pastith | import javax.servlet.ServletException; |
75 | 14ad7326 | pastith | import javax.servlet.ServletOutputStream; |
76 | 14ad7326 | pastith | import javax.servlet.UnavailableException; |
77 | 14ad7326 | pastith | import javax.servlet.http.HttpServlet; |
78 | 14ad7326 | pastith | import javax.servlet.http.HttpServletRequest; |
79 | 14ad7326 | pastith | import javax.servlet.http.HttpServletResponse; |
80 | 14ad7326 | pastith | import javax.xml.parsers.DocumentBuilder; |
81 | 14ad7326 | pastith | import javax.xml.parsers.DocumentBuilderFactory; |
82 | 14ad7326 | pastith | import javax.xml.parsers.ParserConfigurationException; |
83 | 14ad7326 | pastith | |
84 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.server.rest.LockInfo; |
85 | af6aa461 | Christos V. Stathis | import gr.ebs.gss.server.rest.WebdavStatus; |
86 | 14ad7326 | pastith | import org.apache.commons.httpclient.HttpStatus; |
87 | 14ad7326 | pastith | import org.apache.commons.logging.Log; |
88 | 14ad7326 | pastith | import org.apache.commons.logging.LogFactory; |
89 | 14ad7326 | pastith | import org.w3c.dom.Document; |
90 | 14ad7326 | pastith | import org.w3c.dom.Element; |
91 | 14ad7326 | pastith | import org.w3c.dom.Node; |
92 | 14ad7326 | pastith | import org.w3c.dom.NodeList; |
93 | 14ad7326 | pastith | import org.xml.sax.EntityResolver; |
94 | 14ad7326 | pastith | import org.xml.sax.InputSource; |
95 | 14ad7326 | pastith | import org.xml.sax.SAXException; |
96 | 14ad7326 | pastith | |
97 | 14ad7326 | pastith | /**
|
98 | 14ad7326 | pastith | * The implementation of the WebDAV service.
|
99 | 14ad7326 | pastith | *
|
100 | 14ad7326 | pastith | * @author past
|
101 | 14ad7326 | pastith | */
|
102 | 14ad7326 | pastith | public class Webdav extends HttpServlet { |
103 | 14ad7326 | pastith | |
104 | 14ad7326 | pastith | /**
|
105 | 14ad7326 | pastith | * The request attribute containing the user who owns the requested
|
106 | 14ad7326 | pastith | * namespace.
|
107 | 14ad7326 | pastith | */
|
108 | 14ad7326 | pastith | protected static final String OWNER_ATTRIBUTE = "owner"; |
109 | 14ad7326 | pastith | |
110 | 14ad7326 | pastith | /**
|
111 | 14ad7326 | pastith | * The request attribute containing the user making the request.
|
112 | 14ad7326 | pastith | */
|
113 | 14ad7326 | pastith | protected static final String USER_ATTRIBUTE = "user"; |
114 | 14ad7326 | pastith | |
115 | 14ad7326 | pastith | /**
|
116 | 14ad7326 | pastith | * The logger.
|
117 | 14ad7326 | pastith | */
|
118 | 14ad7326 | pastith | private static Log logger = LogFactory.getLog(Webdav.class); |
119 | 14ad7326 | pastith | |
120 | 14ad7326 | pastith | /**
|
121 | 14ad7326 | pastith | *
|
122 | 14ad7326 | pastith | */
|
123 | 14ad7326 | pastith | protected static final String METHOD_GET = "GET"; |
124 | 14ad7326 | pastith | |
125 | 14ad7326 | pastith | /**
|
126 | 14ad7326 | pastith | *
|
127 | 14ad7326 | pastith | */
|
128 | 14ad7326 | pastith | protected static final String METHOD_POST = "POST"; |
129 | 14ad7326 | pastith | |
130 | 14ad7326 | pastith | /**
|
131 | 14ad7326 | pastith | *
|
132 | 14ad7326 | pastith | */
|
133 | 14ad7326 | pastith | protected static final String METHOD_PUT = "PUT"; |
134 | 14ad7326 | pastith | |
135 | 14ad7326 | pastith | /**
|
136 | 14ad7326 | pastith | *
|
137 | 14ad7326 | pastith | */
|
138 | 14ad7326 | pastith | protected static final String METHOD_DELETE = "DELETE"; |
139 | 14ad7326 | pastith | |
140 | 14ad7326 | pastith | /**
|
141 | 14ad7326 | pastith | *
|
142 | 14ad7326 | pastith | */
|
143 | 14ad7326 | pastith | protected static final String METHOD_HEAD = "HEAD"; |
144 | 14ad7326 | pastith | |
145 | 14ad7326 | pastith | /**
|
146 | 14ad7326 | pastith | *
|
147 | 14ad7326 | pastith | */
|
148 | 14ad7326 | pastith | private static final String METHOD_OPTIONS = "OPTIONS"; |
149 | 14ad7326 | pastith | |
150 | 14ad7326 | pastith | /**
|
151 | 14ad7326 | pastith | *
|
152 | 14ad7326 | pastith | */
|
153 | 14ad7326 | pastith | private static final String METHOD_PROPFIND = "PROPFIND"; |
154 | 14ad7326 | pastith | |
155 | 14ad7326 | pastith | /**
|
156 | 14ad7326 | pastith | *
|
157 | 14ad7326 | pastith | */
|
158 | 14ad7326 | pastith | private static final String METHOD_PROPPATCH = "PROPPATCH"; |
159 | 14ad7326 | pastith | |
160 | 14ad7326 | pastith | /**
|
161 | 14ad7326 | pastith | *
|
162 | 14ad7326 | pastith | */
|
163 | 14ad7326 | pastith | private static final String METHOD_MKCOL = "MKCOL"; |
164 | 14ad7326 | pastith | |
165 | 14ad7326 | pastith | /**
|
166 | 14ad7326 | pastith | *
|
167 | 14ad7326 | pastith | */
|
168 | 14ad7326 | pastith | private static final String METHOD_COPY = "COPY"; |
169 | 14ad7326 | pastith | |
170 | 14ad7326 | pastith | /**
|
171 | 14ad7326 | pastith | *
|
172 | 14ad7326 | pastith | */
|
173 | 14ad7326 | pastith | private static final String METHOD_MOVE = "MOVE"; |
174 | 14ad7326 | pastith | |
175 | 14ad7326 | pastith | /**
|
176 | 14ad7326 | pastith | *
|
177 | 14ad7326 | pastith | */
|
178 | 14ad7326 | pastith | private static final String METHOD_LOCK = "LOCK"; |
179 | 14ad7326 | pastith | |
180 | 14ad7326 | pastith | /**
|
181 | 14ad7326 | pastith | *
|
182 | 14ad7326 | pastith | */
|
183 | 14ad7326 | pastith | private static final String METHOD_UNLOCK = "UNLOCK"; |
184 | 14ad7326 | pastith | |
185 | 14ad7326 | pastith | /**
|
186 | 14ad7326 | pastith | * Default depth is infinite.
|
187 | 14ad7326 | pastith | */
|
188 | 14ad7326 | pastith | static final int INFINITY = 3; // To limit tree browsing a bit |
189 | 14ad7326 | pastith | |
190 | 14ad7326 | pastith | /**
|
191 | 14ad7326 | pastith | * PROPFIND - Specify a property mask.
|
192 | 14ad7326 | pastith | */
|
193 | 14ad7326 | pastith | private static final int FIND_BY_PROPERTY = 0; |
194 | 14ad7326 | pastith | |
195 | 14ad7326 | pastith | /**
|
196 | 14ad7326 | pastith | * PROPFIND - Display all properties.
|
197 | 14ad7326 | pastith | */
|
198 | 14ad7326 | pastith | private static final int FIND_ALL_PROP = 1; |
199 | 14ad7326 | pastith | |
200 | 14ad7326 | pastith | /**
|
201 | 14ad7326 | pastith | * PROPFIND - Return property names.
|
202 | 14ad7326 | pastith | */
|
203 | 14ad7326 | pastith | private static final int FIND_PROPERTY_NAMES = 2; |
204 | 14ad7326 | pastith | |
205 | 14ad7326 | pastith | /**
|
206 | 14ad7326 | pastith | * Default namespace.
|
207 | 14ad7326 | pastith | */
|
208 | 14ad7326 | pastith | private static final String DEFAULT_NAMESPACE = "DAV:"; |
209 | 14ad7326 | pastith | |
210 | 14ad7326 | pastith | /**
|
211 | 14ad7326 | pastith | * Create a new lock.
|
212 | 14ad7326 | pastith | */
|
213 | 14ad7326 | pastith | private static final int LOCK_CREATION = 0; |
214 | 14ad7326 | pastith | |
215 | 14ad7326 | pastith | /**
|
216 | 14ad7326 | pastith | * Refresh lock.
|
217 | 14ad7326 | pastith | */
|
218 | 14ad7326 | pastith | private static final int LOCK_REFRESH = 1; |
219 | 14ad7326 | pastith | |
220 | 14ad7326 | pastith | /**
|
221 | 14ad7326 | pastith | * Default lock timeout value.
|
222 | 14ad7326 | pastith | */
|
223 | 14ad7326 | pastith | private static final int DEFAULT_TIMEOUT = 3600; |
224 | 14ad7326 | pastith | |
225 | 14ad7326 | pastith | /**
|
226 | 14ad7326 | pastith | * Maximum lock timeout.
|
227 | 14ad7326 | pastith | */
|
228 | 14ad7326 | pastith | private static final int MAX_TIMEOUT = 604800; |
229 | 14ad7326 | pastith | |
230 | 14ad7326 | pastith | /**
|
231 | 14ad7326 | pastith | * Size of file transfer buffer in bytes.
|
232 | 14ad7326 | pastith | */
|
233 | 14ad7326 | pastith | private static final int BUFFER_SIZE = 4096; |
234 | 14ad7326 | pastith | |
235 | 14ad7326 | pastith | /**
|
236 | 14ad7326 | pastith | * The output buffer size to use when serving resources.
|
237 | 14ad7326 | pastith | */
|
238 | 14ad7326 | pastith | protected int output = 2048; |
239 | 14ad7326 | pastith | |
240 | 14ad7326 | pastith | /**
|
241 | 14ad7326 | pastith | * The input buffer size to use when serving resources.
|
242 | 14ad7326 | pastith | */
|
243 | 14ad7326 | pastith | private int input = 2048; |
244 | 14ad7326 | pastith | |
245 | 14ad7326 | pastith | /**
|
246 | 14ad7326 | pastith | * MIME multipart separation string
|
247 | 14ad7326 | pastith | */
|
248 | 14ad7326 | pastith | protected static final String mimeSeparation = "GSS_MIME_BOUNDARY"; |
249 | 14ad7326 | pastith | |
250 | 14ad7326 | pastith | /**
|
251 | 14ad7326 | pastith | * Simple date format for the creation date ISO representation (partial).
|
252 | 14ad7326 | pastith | */
|
253 | 14ad7326 | pastith | private static final SimpleDateFormat creationDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); |
254 | 14ad7326 | pastith | |
255 | 14ad7326 | pastith | /**
|
256 | 14ad7326 | pastith | * HTTP date format.
|
257 | 14ad7326 | pastith | */
|
258 | 14ad7326 | pastith | private static final SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); |
259 | 14ad7326 | pastith | |
260 | 14ad7326 | pastith | /**
|
261 | 14ad7326 | pastith | * Array containing the safe characters set.
|
262 | 14ad7326 | pastith | */
|
263 | 14ad7326 | pastith | private static URLEncoder urlEncoder; |
264 | 14ad7326 | pastith | |
265 | 14ad7326 | pastith | /**
|
266 | 14ad7326 | pastith | * File encoding to be used when reading static files. If none is specified
|
267 | 14ad7326 | pastith | * the platform default is used.
|
268 | 14ad7326 | pastith | */
|
269 | 14ad7326 | pastith | private String fileEncoding = null; |
270 | 14ad7326 | pastith | |
271 | 14ad7326 | pastith | /**
|
272 | 14ad7326 | pastith | * The style sheet for displaying the directory listings.
|
273 | 14ad7326 | pastith | */
|
274 | 14ad7326 | pastith | private static final String GSS_CSS = "H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} " + "H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} " + "H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} " + "BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} " + "B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} " + "P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}" + "A {color : black;}" + "A.name {color : black;}" + "HR {color : #525D76;}"; |
275 | 14ad7326 | pastith | |
276 | 14ad7326 | pastith | /**
|
277 | 14ad7326 | pastith | * Secret information used to generate reasonably secure lock ids.
|
278 | 14ad7326 | pastith | */
|
279 | 14ad7326 | pastith | private String secret = "gss-webdav"; |
280 | 14ad7326 | pastith | |
281 | 14ad7326 | pastith | |
282 | 14ad7326 | pastith | /**
|
283 | 14ad7326 | pastith | * Full range marker.
|
284 | 14ad7326 | pastith | */
|
285 | 14ad7326 | pastith | protected static ArrayList FULL = new ArrayList(); |
286 | 14ad7326 | pastith | |
287 | 14ad7326 | pastith | /**
|
288 | 14ad7326 | pastith | * MD5 message digest provider.
|
289 | 14ad7326 | pastith | */
|
290 | 14ad7326 | pastith | protected static MessageDigest md5Helper; |
291 | 14ad7326 | pastith | |
292 | 14ad7326 | pastith | /**
|
293 | 14ad7326 | pastith | * The MD5 helper object for this class.
|
294 | 14ad7326 | pastith | */
|
295 | 14ad7326 | pastith | protected static final MD5Encoder md5Encoder = new MD5Encoder(); |
296 | 14ad7326 | pastith | |
297 | 14ad7326 | pastith | /**
|
298 | 14ad7326 | pastith | * GMT timezone - all HTTP dates are on GMT
|
299 | 14ad7326 | pastith | */
|
300 | 14ad7326 | pastith | static {
|
301 | 14ad7326 | pastith | creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); |
302 | 14ad7326 | pastith | urlEncoder = new URLEncoder(); |
303 | 14ad7326 | pastith | urlEncoder.addSafeCharacter('-');
|
304 | 14ad7326 | pastith | urlEncoder.addSafeCharacter('_');
|
305 | 14ad7326 | pastith | urlEncoder.addSafeCharacter('.');
|
306 | 14ad7326 | pastith | urlEncoder.addSafeCharacter('*');
|
307 | 14ad7326 | pastith | urlEncoder.addSafeCharacter('/');
|
308 | 14ad7326 | pastith | } |
309 | 14ad7326 | pastith | |
310 | 14ad7326 | pastith | @Override
|
311 | 14ad7326 | pastith | public void init() throws ServletException { |
312 | 14ad7326 | pastith | if (getServletConfig().getInitParameter("input") != null) |
313 | 14ad7326 | pastith | input = Integer.parseInt(getServletConfig().getInitParameter("input")); |
314 | 14ad7326 | pastith | |
315 | 14ad7326 | pastith | if (getServletConfig().getInitParameter("output") != null) |
316 | 14ad7326 | pastith | output = Integer.parseInt(getServletConfig().getInitParameter("output")); |
317 | 14ad7326 | pastith | |
318 | 14ad7326 | pastith | fileEncoding = getServletConfig().getInitParameter("fileEncoding");
|
319 | 14ad7326 | pastith | |
320 | 14ad7326 | pastith | // Sanity check on the specified buffer sizes
|
321 | 14ad7326 | pastith | if (input < 256) |
322 | 14ad7326 | pastith | input = 256;
|
323 | 14ad7326 | pastith | if (output < 256) |
324 | 14ad7326 | pastith | output = 256;
|
325 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
326 | 14ad7326 | pastith | logger.debug("Input buffer size=" + input + ", output buffer size=" + output); |
327 | 14ad7326 | pastith | |
328 | 14ad7326 | pastith | if (getServletConfig().getInitParameter("secret") != null) |
329 | 14ad7326 | pastith | secret = getServletConfig().getInitParameter("secret");
|
330 | 14ad7326 | pastith | |
331 | 14ad7326 | pastith | // Load the MD5 helper used to calculate signatures.
|
332 | 14ad7326 | pastith | try {
|
333 | 14ad7326 | pastith | md5Helper = MessageDigest.getInstance("MD5"); |
334 | 14ad7326 | pastith | } catch (NoSuchAlgorithmException e) { |
335 | 14ad7326 | pastith | throw new UnavailableException("No MD5"); |
336 | 14ad7326 | pastith | } |
337 | 14ad7326 | pastith | } |
338 | 14ad7326 | pastith | |
339 | 14ad7326 | pastith | /**
|
340 | 14ad7326 | pastith | * A helper method that retrieves a reference to the ExternalAPI bean and
|
341 | 14ad7326 | pastith | * stores it for future use.
|
342 | 14ad7326 | pastith | *
|
343 | 14ad7326 | pastith | * @return an ExternalAPI instance
|
344 | 14ad7326 | pastith | * @throws RpcException in case an error occurs
|
345 | 14ad7326 | pastith | */
|
346 | 14ad7326 | pastith | protected ExternalAPI getService() throws RpcException { |
347 | 14ad7326 | pastith | try {
|
348 | 14ad7326 | pastith | final Context ctx = new InitialContext(); |
349 | bde4eafb | pastith | final Object ref = ctx.lookup(getConfiguration().getString("externalApiPath")); |
350 | 14ad7326 | pastith | return (ExternalAPI) PortableRemoteObject.narrow(ref, ExternalAPI.class); |
351 | 14ad7326 | pastith | } catch (final NamingException e) { |
352 | 14ad7326 | pastith | logger.error("Unable to retrieve the ExternalAPI EJB", e);
|
353 | 14ad7326 | pastith | throw new RpcException("An error occurred while contacting the naming service"); |
354 | 14ad7326 | pastith | } |
355 | 14ad7326 | pastith | } |
356 | 14ad7326 | pastith | |
357 | 2f1a60e0 | Dimitris Routsis | private void updateAccounting(final User user, final Date date, final long bandwidthDiff) { |
358 | 2f1a60e0 | Dimitris Routsis | try {
|
359 | 2f1a60e0 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
360 | 2f1a60e0 | Dimitris Routsis | @Override
|
361 | 2f1a60e0 | Dimitris Routsis | public Void call() throws Exception { |
362 | 2f1a60e0 | Dimitris Routsis | getService().updateAccounting(user, date, bandwidthDiff); |
363 | 2f1a60e0 | Dimitris Routsis | return null; |
364 | 2f1a60e0 | Dimitris Routsis | } |
365 | 2f1a60e0 | Dimitris Routsis | }); |
366 | 2f1a60e0 | Dimitris Routsis | } catch (RuntimeException e) { |
367 | 2f1a60e0 | Dimitris Routsis | throw e;
|
368 | 2f1a60e0 | Dimitris Routsis | } catch (Exception e) { |
369 | 2f1a60e0 | Dimitris Routsis | // updateAccounting() doesn't throw any checked exceptions
|
370 | 2f1a60e0 | Dimitris Routsis | assert false; |
371 | 2f1a60e0 | Dimitris Routsis | } |
372 | 2f1a60e0 | Dimitris Routsis | } |
373 | 2f1a60e0 | Dimitris Routsis | |
374 | 14ad7326 | pastith | @Override
|
375 | 14ad7326 | pastith | public void service(final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { |
376 | 14ad7326 | pastith | String method = request.getMethod();
|
377 | 14ad7326 | pastith | |
378 | 14ad7326 | pastith | if (logger.isDebugEnabled()) {
|
379 | 14ad7326 | pastith | String path = request.getPathInfo();
|
380 | 14ad7326 | pastith | if (path == null) |
381 | 14ad7326 | pastith | path = request.getServletPath(); |
382 | 14ad7326 | pastith | if (path == null || path.equals("")) |
383 | 14ad7326 | pastith | path = "/";
|
384 | 14ad7326 | pastith | logger.debug("[" + method + "] " + path); |
385 | 14ad7326 | pastith | } |
386 | 14ad7326 | pastith | |
387 | 14ad7326 | pastith | try {
|
388 | 14ad7326 | pastith | User user = null;
|
389 | 14ad7326 | pastith | if (request.getUserPrincipal() != null) { // Let unauthenticated |
390 | 14ad7326 | pastith | // OPTIONS go through;
|
391 | 14ad7326 | pastith | // all others will be
|
392 | 14ad7326 | pastith | // blocked by
|
393 | 14ad7326 | pastith | // authentication anyway
|
394 | 14ad7326 | pastith | // before we get here.
|
395 | 14ad7326 | pastith | user = getService().findUser(request.getUserPrincipal().getName()); |
396 | 14ad7326 | pastith | if (user == null) { |
397 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
398 | 14ad7326 | pastith | return;
|
399 | 14ad7326 | pastith | } |
400 | 14ad7326 | pastith | } |
401 | 14ad7326 | pastith | request.setAttribute(USER_ATTRIBUTE, user); |
402 | 14ad7326 | pastith | request.setAttribute(OWNER_ATTRIBUTE, user); |
403 | 14ad7326 | pastith | } catch (RpcException e) {
|
404 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
405 | 14ad7326 | pastith | return;
|
406 | 14ad7326 | pastith | } |
407 | 14ad7326 | pastith | if (method.equals(METHOD_GET))
|
408 | 14ad7326 | pastith | doGet(request, response); |
409 | 14ad7326 | pastith | else if (method.equals(METHOD_POST)) |
410 | 14ad7326 | pastith | doPost(request, response); |
411 | 14ad7326 | pastith | else if (method.equals(METHOD_PUT)) |
412 | 14ad7326 | pastith | doPut(request, response); |
413 | 14ad7326 | pastith | else if (method.equals(METHOD_DELETE)) |
414 | 14ad7326 | pastith | doDelete(request, response); |
415 | 14ad7326 | pastith | else if (method.equals(METHOD_HEAD)) |
416 | 14ad7326 | pastith | doHead(request, response); |
417 | 14ad7326 | pastith | else if (method.equals(METHOD_PROPFIND)) |
418 | 14ad7326 | pastith | doPropfind(request, response); |
419 | 14ad7326 | pastith | else if (method.equals(METHOD_PROPPATCH)) |
420 | 14ad7326 | pastith | doProppatch(request, response); |
421 | 14ad7326 | pastith | else if (method.equals(METHOD_MKCOL)) |
422 | 14ad7326 | pastith | doMkcol(request, response); |
423 | 14ad7326 | pastith | else if (method.equals(METHOD_COPY)) |
424 | 14ad7326 | pastith | doCopy(request, response); |
425 | 14ad7326 | pastith | else if (method.equals(METHOD_MOVE)) |
426 | 14ad7326 | pastith | doMove(request, response); |
427 | 14ad7326 | pastith | else if (method.equals(METHOD_LOCK)) |
428 | 14ad7326 | pastith | doLock(request, response); |
429 | 14ad7326 | pastith | else if (method.equals(METHOD_UNLOCK)) |
430 | 14ad7326 | pastith | doUnlock(request, response); |
431 | 14ad7326 | pastith | else if (method.equals(METHOD_OPTIONS)) |
432 | 14ad7326 | pastith | doOptions(request, response); |
433 | 14ad7326 | pastith | else
|
434 | 14ad7326 | pastith | // DefaultServlet processing for TRACE, etc.
|
435 | 14ad7326 | pastith | super.service(request, response);
|
436 | 14ad7326 | pastith | } |
437 | 14ad7326 | pastith | |
438 | 14ad7326 | pastith | @Override
|
439 | 14ad7326 | pastith | protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
440 | 14ad7326 | pastith | resp.addHeader("DAV", "1,2"); |
441 | 14ad7326 | pastith | StringBuffer methodsAllowed = new StringBuffer(); |
442 | 14ad7326 | pastith | try {
|
443 | 14ad7326 | pastith | methodsAllowed = determineMethodsAllowed(req); |
444 | 14ad7326 | pastith | } catch (RpcException e) {
|
445 | 14ad7326 | pastith | resp.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR); |
446 | 14ad7326 | pastith | return;
|
447 | 14ad7326 | pastith | } |
448 | 14ad7326 | pastith | resp.addHeader("Allow", methodsAllowed.toString());
|
449 | 14ad7326 | pastith | resp.addHeader("MS-Author-Via", "DAV"); |
450 | 14ad7326 | pastith | } |
451 | 14ad7326 | pastith | |
452 | 14ad7326 | pastith | /**
|
453 | 14ad7326 | pastith | * Implement the PROPFIND method.
|
454 | 14ad7326 | pastith | *
|
455 | 14ad7326 | pastith | * @param req the HTTP request
|
456 | 14ad7326 | pastith | * @param resp the HTTP response
|
457 | 14ad7326 | pastith | * @throws ServletException
|
458 | 14ad7326 | pastith | * @throws IOException
|
459 | 14ad7326 | pastith | */
|
460 | 14ad7326 | pastith | private void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
461 | 14ad7326 | pastith | String path = getRelativePath(req);
|
462 | 14ad7326 | pastith | if (path.endsWith("/") && !path.equals("/")) |
463 | 14ad7326 | pastith | path = path.substring(0, path.length() - 1); |
464 | 14ad7326 | pastith | |
465 | 14ad7326 | pastith | if (path.toUpperCase().startsWith("/WEB-INF") || path.toUpperCase().startsWith("/META-INF")) { |
466 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN); |
467 | 14ad7326 | pastith | return;
|
468 | 14ad7326 | pastith | } |
469 | 14ad7326 | pastith | |
470 | 14ad7326 | pastith | // Properties which are to be displayed.
|
471 | 14ad7326 | pastith | Vector<String> properties = null; |
472 | 14ad7326 | pastith | // Propfind depth
|
473 | 14ad7326 | pastith | int depth = INFINITY;
|
474 | 14ad7326 | pastith | // Propfind type
|
475 | 14ad7326 | pastith | int type = FIND_ALL_PROP;
|
476 | 14ad7326 | pastith | |
477 | 14ad7326 | pastith | String depthStr = req.getHeader("Depth"); |
478 | 14ad7326 | pastith | |
479 | 14ad7326 | pastith | if (depthStr == null) |
480 | 14ad7326 | pastith | depth = INFINITY; |
481 | 14ad7326 | pastith | else if (depthStr.equals("0")) |
482 | 14ad7326 | pastith | depth = 0;
|
483 | 14ad7326 | pastith | else if (depthStr.equals("1")) |
484 | 14ad7326 | pastith | depth = 1;
|
485 | 14ad7326 | pastith | else if (depthStr.equals("infinity")) |
486 | 14ad7326 | pastith | depth = INFINITY; |
487 | 14ad7326 | pastith | |
488 | 14ad7326 | pastith | Node propNode = null;
|
489 | 14ad7326 | pastith | |
490 | 14ad7326 | pastith | if (req.getInputStream().available() > 0) { |
491 | 14ad7326 | pastith | DocumentBuilder documentBuilder = getDocumentBuilder();
|
492 | 14ad7326 | pastith | |
493 | 14ad7326 | pastith | try {
|
494 | 14ad7326 | pastith | Document document = documentBuilder.parse(new InputSource(req.getInputStream())); |
495 | 14ad7326 | pastith | |
496 | 14ad7326 | pastith | // Get the root element of the document
|
497 | 14ad7326 | pastith | Element rootElement = document.getDocumentElement();
|
498 | 14ad7326 | pastith | NodeList childList = rootElement.getChildNodes(); |
499 | 14ad7326 | pastith | |
500 | 14ad7326 | pastith | for (int i = 0; i < childList.getLength(); i++) { |
501 | 14ad7326 | pastith | Node currentNode = childList.item(i); |
502 | 14ad7326 | pastith | switch (currentNode.getNodeType()) {
|
503 | 14ad7326 | pastith | case Node.TEXT_NODE:
|
504 | 14ad7326 | pastith | break;
|
505 | 14ad7326 | pastith | case Node.ELEMENT_NODE:
|
506 | 14ad7326 | pastith | if (currentNode.getNodeName().endsWith("prop")) { |
507 | 14ad7326 | pastith | type = FIND_BY_PROPERTY; |
508 | 14ad7326 | pastith | propNode = currentNode; |
509 | 14ad7326 | pastith | } |
510 | 14ad7326 | pastith | if (currentNode.getNodeName().endsWith("propname")) |
511 | 14ad7326 | pastith | type = FIND_PROPERTY_NAMES; |
512 | 14ad7326 | pastith | if (currentNode.getNodeName().endsWith("allprop")) |
513 | 14ad7326 | pastith | type = FIND_ALL_PROP; |
514 | 14ad7326 | pastith | break;
|
515 | 14ad7326 | pastith | } |
516 | 14ad7326 | pastith | } |
517 | 14ad7326 | pastith | } catch (SAXException e) {
|
518 | 14ad7326 | pastith | // Something went wrong - use the defaults.
|
519 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
520 | 14ad7326 | pastith | logger.debug(e.getMessage()); |
521 | 14ad7326 | pastith | } catch (IOException e) { |
522 | 14ad7326 | pastith | // Something went wrong - use the defaults.
|
523 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
524 | 14ad7326 | pastith | logger.debug(e.getMessage()); |
525 | 14ad7326 | pastith | } |
526 | 14ad7326 | pastith | } |
527 | 14ad7326 | pastith | |
528 | 14ad7326 | pastith | if (type == FIND_BY_PROPERTY) {
|
529 | 14ad7326 | pastith | properties = new Vector<String>(); |
530 | 14ad7326 | pastith | NodeList childList = propNode.getChildNodes(); |
531 | 14ad7326 | pastith | |
532 | 14ad7326 | pastith | for (int i = 0; i < childList.getLength(); i++) { |
533 | 14ad7326 | pastith | Node currentNode = childList.item(i); |
534 | 14ad7326 | pastith | switch (currentNode.getNodeType()) {
|
535 | 14ad7326 | pastith | case Node.TEXT_NODE:
|
536 | 14ad7326 | pastith | break;
|
537 | 14ad7326 | pastith | case Node.ELEMENT_NODE:
|
538 | 14ad7326 | pastith | String nodeName = currentNode.getNodeName();
|
539 | 14ad7326 | pastith | String propertyName = null; |
540 | 14ad7326 | pastith | if (nodeName.indexOf(':') != -1) |
541 | 14ad7326 | pastith | propertyName = nodeName.substring(nodeName.indexOf(':') + 1); |
542 | 14ad7326 | pastith | else
|
543 | 14ad7326 | pastith | propertyName = nodeName; |
544 | 14ad7326 | pastith | // href is a live property which is handled differently
|
545 | 14ad7326 | pastith | properties.addElement(propertyName); |
546 | 14ad7326 | pastith | break;
|
547 | 14ad7326 | pastith | } |
548 | 14ad7326 | pastith | } |
549 | 14ad7326 | pastith | } |
550 | 14ad7326 | pastith | User user = getUser(req); |
551 | 14ad7326 | pastith | boolean exists = true; |
552 | 14ad7326 | pastith | Object object = null; |
553 | 14ad7326 | pastith | try {
|
554 | 68410d59 | pastith | object = getService().getResourceAtPath(user.getId(), path, true);
|
555 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
556 | 14ad7326 | pastith | exists = false;
|
557 | 14ad7326 | pastith | } catch (RpcException e) {
|
558 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
559 | 14ad7326 | pastith | return;
|
560 | 14ad7326 | pastith | } |
561 | 14ad7326 | pastith | if (!exists) {
|
562 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_FOUND, path); |
563 | 14ad7326 | pastith | return;
|
564 | 14ad7326 | pastith | } |
565 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_MULTI_STATUS); |
566 | 14ad7326 | pastith | resp.setContentType("text/xml; charset=UTF-8");
|
567 | 14ad7326 | pastith | // Create multistatus object
|
568 | 14ad7326 | pastith | XMLWriter generatedXML = new XMLWriter(resp.getWriter());
|
569 | 14ad7326 | pastith | generatedXML.writeXMLHeader(); |
570 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:multistatus" + generateNamespaceDeclarations(), XMLWriter.OPENING); |
571 | 14ad7326 | pastith | if (depth == 0) |
572 | 14ad7326 | pastith | parseProperties(req, generatedXML, path, type, properties, object); |
573 | 14ad7326 | pastith | else {
|
574 | 14ad7326 | pastith | // The stack always contains the object of the current level
|
575 | 14ad7326 | pastith | Stack<String> stack = new Stack<String>(); |
576 | 14ad7326 | pastith | stack.push(path); |
577 | 14ad7326 | pastith | |
578 | 14ad7326 | pastith | // Stack of the objects one level below
|
579 | 14ad7326 | pastith | Stack<String> stackBelow = new Stack<String>(); |
580 | 14ad7326 | pastith | while (!stack.isEmpty() && depth >= 0) { |
581 | 14ad7326 | pastith | String currentPath = stack.pop();
|
582 | 14ad7326 | pastith | try {
|
583 | 68410d59 | pastith | object = getService().getResourceAtPath(user.getId(), currentPath, true);
|
584 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
585 | 14ad7326 | pastith | continue;
|
586 | 14ad7326 | pastith | } catch (RpcException e) {
|
587 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
588 | 14ad7326 | pastith | return;
|
589 | 14ad7326 | pastith | } |
590 | 14ad7326 | pastith | parseProperties(req, generatedXML, currentPath, type, properties, object); |
591 | f7c44c33 | fstamatelopoulos | if (object instanceof Folder && depth > 0) { |
592 | f7c44c33 | fstamatelopoulos | Folder folderLocal = (Folder) object; |
593 | 14ad7326 | pastith | // Retrieve the subfolders.
|
594 | f7c44c33 | fstamatelopoulos | List subfolders = folderLocal.getSubfolders();
|
595 | 14ad7326 | pastith | Iterator iter = subfolders.iterator();
|
596 | 14ad7326 | pastith | while (iter.hasNext()) {
|
597 | f7c44c33 | fstamatelopoulos | Folder f = (Folder) iter.next(); |
598 | 14ad7326 | pastith | String newPath = currentPath;
|
599 | 14ad7326 | pastith | if (!newPath.endsWith("/")) |
600 | 14ad7326 | pastith | newPath += "/";
|
601 | 14ad7326 | pastith | newPath += f.getName(); |
602 | 14ad7326 | pastith | stackBelow.push(newPath); |
603 | 14ad7326 | pastith | } |
604 | 14ad7326 | pastith | // Retrieve the files.
|
605 | f7c44c33 | fstamatelopoulos | List<FileHeader> files;
|
606 | 14ad7326 | pastith | try {
|
607 | f7c44c33 | fstamatelopoulos | files = getService().getFiles(user.getId(), folderLocal.getId(), true);
|
608 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
609 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
610 | 14ad7326 | pastith | return;
|
611 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
612 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_FORBIDDEN, path); |
613 | 14ad7326 | pastith | return;
|
614 | 14ad7326 | pastith | } catch (RpcException e) {
|
615 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
616 | 14ad7326 | pastith | return;
|
617 | 14ad7326 | pastith | } |
618 | f7c44c33 | fstamatelopoulos | for (FileHeader file : files) {
|
619 | 14ad7326 | pastith | String newPath = currentPath;
|
620 | 14ad7326 | pastith | if (!newPath.endsWith("/")) |
621 | 14ad7326 | pastith | newPath += "/";
|
622 | 14ad7326 | pastith | newPath += file.getName(); |
623 | 14ad7326 | pastith | stackBelow.push(newPath); |
624 | 14ad7326 | pastith | } |
625 | 14ad7326 | pastith | } |
626 | 14ad7326 | pastith | if (stack.isEmpty()) {
|
627 | 14ad7326 | pastith | depth--; |
628 | 14ad7326 | pastith | stack = stackBelow; |
629 | 14ad7326 | pastith | stackBelow = new Stack<String>(); |
630 | 14ad7326 | pastith | } |
631 | 14ad7326 | pastith | generatedXML.sendData(); |
632 | 14ad7326 | pastith | } |
633 | 14ad7326 | pastith | } |
634 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:multistatus", XMLWriter.CLOSING); |
635 | 14ad7326 | pastith | generatedXML.sendData(); |
636 | 14ad7326 | pastith | } |
637 | 14ad7326 | pastith | |
638 | 14ad7326 | pastith | /**
|
639 | 14ad7326 | pastith | * PROPPATCH Method.
|
640 | 14ad7326 | pastith | *
|
641 | 14ad7326 | pastith | * @param req the HTTP request
|
642 | 14ad7326 | pastith | * @param resp the HTTP response
|
643 | 14ad7326 | pastith | * @throws IOException if an error occurs while sending the response
|
644 | 14ad7326 | pastith | */
|
645 | 14ad7326 | pastith | private void doProppatch(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
646 | 14ad7326 | pastith | if (isLocked(req)) {
|
647 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_LOCKED); |
648 | 14ad7326 | pastith | return;
|
649 | 14ad7326 | pastith | } |
650 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); |
651 | 14ad7326 | pastith | } |
652 | 14ad7326 | pastith | |
653 | 14ad7326 | pastith | @Override
|
654 | 86c951b2 | Panagiotis Astithas | protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
655 | 14ad7326 | pastith | if (isLocked(req)) {
|
656 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_LOCKED); |
657 | 14ad7326 | pastith | return;
|
658 | 14ad7326 | pastith | } |
659 | 14ad7326 | pastith | deleteResource(req, resp); |
660 | 14ad7326 | pastith | } |
661 | 14ad7326 | pastith | |
662 | 14ad7326 | pastith | @Override
|
663 | 14ad7326 | pastith | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { |
664 | 14ad7326 | pastith | // Serve the requested resource, including the data content
|
665 | 14ad7326 | pastith | try {
|
666 | 14ad7326 | pastith | serveResource(req, resp, true);
|
667 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
668 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_FOUND); |
669 | 14ad7326 | pastith | return;
|
670 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
671 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_FORBIDDEN); |
672 | 14ad7326 | pastith | return;
|
673 | 14ad7326 | pastith | } catch (RpcException e) {
|
674 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
675 | 14ad7326 | pastith | return;
|
676 | 14ad7326 | pastith | } |
677 | 14ad7326 | pastith | } |
678 | 14ad7326 | pastith | |
679 | 14ad7326 | pastith | @Override
|
680 | 14ad7326 | pastith | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
681 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); |
682 | 14ad7326 | pastith | } |
683 | 14ad7326 | pastith | |
684 | 14ad7326 | pastith | @Override
|
685 | 14ad7326 | pastith | protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
686 | 14ad7326 | pastith | if (isLocked(req)) {
|
687 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_LOCKED); |
688 | 14ad7326 | pastith | return;
|
689 | 14ad7326 | pastith | } |
690 | 14ad7326 | pastith | |
691 | 3b6b7f25 | Dimitris Routsis | final User user = getUser(req);
|
692 | 14ad7326 | pastith | String path = getRelativePath(req);
|
693 | 14ad7326 | pastith | boolean exists = true; |
694 | 14ad7326 | pastith | Object resource = null; |
695 | f7c44c33 | fstamatelopoulos | FileHeader file = null;
|
696 | 14ad7326 | pastith | try {
|
697 | 68410d59 | pastith | resource = getService().getResourceAtPath(user.getId(), path, true);
|
698 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
699 | 14ad7326 | pastith | exists = false;
|
700 | 14ad7326 | pastith | } catch (RpcException e) {
|
701 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
702 | 14ad7326 | pastith | return;
|
703 | 14ad7326 | pastith | } |
704 | 14ad7326 | pastith | |
705 | 14ad7326 | pastith | if (exists)
|
706 | f7c44c33 | fstamatelopoulos | if (resource instanceof FileHeader) |
707 | f7c44c33 | fstamatelopoulos | file = (FileHeader) resource; |
708 | 14ad7326 | pastith | else {
|
709 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_CONFLICT); |
710 | 14ad7326 | pastith | return;
|
711 | 14ad7326 | pastith | } |
712 | 14ad7326 | pastith | boolean result = true; |
713 | 14ad7326 | pastith | |
714 | 14ad7326 | pastith | // Temporary content file used to support partial PUT.
|
715 | 14ad7326 | pastith | File contentFile = null; |
716 | 14ad7326 | pastith | |
717 | 14ad7326 | pastith | Range range = parseContentRange(req, resp); |
718 | 14ad7326 | pastith | |
719 | 14ad7326 | pastith | InputStream resourceInputStream = null; |
720 | 14ad7326 | pastith | |
721 | 14ad7326 | pastith | // Append data specified in ranges to existing content for this
|
722 | 14ad7326 | pastith | // resource - create a temporary file on the local filesystem to
|
723 | 14ad7326 | pastith | // perform this operation.
|
724 | 14ad7326 | pastith | // Assume just one range is specified for now
|
725 | 14ad7326 | pastith | if (range != null) { |
726 | 14ad7326 | pastith | try {
|
727 | 14ad7326 | pastith | contentFile = executePartialPut(req, range, path); |
728 | 14ad7326 | pastith | } catch (RpcException e) {
|
729 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
730 | 14ad7326 | pastith | return;
|
731 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
732 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_CONFLICT); |
733 | 14ad7326 | pastith | return;
|
734 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
735 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_FORBIDDEN); |
736 | 14ad7326 | pastith | return;
|
737 | 14ad7326 | pastith | } |
738 | 14ad7326 | pastith | resourceInputStream = new FileInputStream(contentFile); |
739 | 14ad7326 | pastith | } else
|
740 | 14ad7326 | pastith | resourceInputStream = req.getInputStream(); |
741 | 14ad7326 | pastith | |
742 | 14ad7326 | pastith | try {
|
743 | 68410d59 | pastith | Object parent = getService().getResourceAtPath(user.getId(), getParentPath(path), true); |
744 | f7c44c33 | fstamatelopoulos | if (!(parent instanceof Folder)) { |
745 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_CONFLICT); |
746 | 14ad7326 | pastith | return;
|
747 | 14ad7326 | pastith | } |
748 | f7c44c33 | fstamatelopoulos | final Folder folderLocal = (Folder) parent;
|
749 | 3b6b7f25 | Dimitris Routsis | final String name = getLastElement(path); |
750 | 3b6b7f25 | Dimitris Routsis | final String mimeType = getServletContext().getMimeType(name); |
751 | b13aae04 | pastith | File uploadedFile = null; |
752 | b13aae04 | pastith | try {
|
753 | b13aae04 | pastith | uploadedFile = getService().uploadFile(resourceInputStream, user.getId()); |
754 | b13aae04 | pastith | } catch (IOException ex) { |
755 | b13aae04 | pastith | throw new GSSIOException(ex, false); |
756 | b13aae04 | pastith | } |
757 | 14ad7326 | pastith | // FIXME: Add attributes
|
758 | f7c44c33 | fstamatelopoulos | FileHeader fileLocal = null;
|
759 | f7c44c33 | fstamatelopoulos | final FileHeader f = file;
|
760 | 3b6b7f25 | Dimitris Routsis | final File uf = uploadedFile; |
761 | 14ad7326 | pastith | if (exists)
|
762 | f7c44c33 | fstamatelopoulos | fileLocal = new TransactionHelper<FileHeader>().tryExecute(new Callable<FileHeader>() { |
763 | 3b6b7f25 | Dimitris Routsis | @Override
|
764 | f7c44c33 | fstamatelopoulos | public FileHeader call() throws Exception { |
765 | 3b6b7f25 | Dimitris Routsis | return getService().updateFileContents(user.getId(), f.getId(), mimeType, uf.length(), uf.getAbsolutePath());
|
766 | 3b6b7f25 | Dimitris Routsis | } |
767 | 3b6b7f25 | Dimitris Routsis | }); |
768 | 14ad7326 | pastith | else
|
769 | f7c44c33 | fstamatelopoulos | fileLocal = new TransactionHelper<FileHeader>().tryExecute(new Callable<FileHeader>() { |
770 | 3b6b7f25 | Dimitris Routsis | @Override
|
771 | f7c44c33 | fstamatelopoulos | public FileHeader call() throws Exception { |
772 | f7c44c33 | fstamatelopoulos | return getService().createFile(user.getId(), folderLocal.getId(), name, mimeType, uf.length(), uf.getAbsolutePath());
|
773 | 3b6b7f25 | Dimitris Routsis | } |
774 | 3b6b7f25 | Dimitris Routsis | }); |
775 | f7c44c33 | fstamatelopoulos | updateAccounting(user, new Date(), fileLocal.getCurrentBody().getFileSize()); |
776 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
777 | 14ad7326 | pastith | result = false;
|
778 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
779 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_FORBIDDEN); |
780 | 14ad7326 | pastith | return;
|
781 | 14ad7326 | pastith | } catch (QuotaExceededException e) {
|
782 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_FORBIDDEN); |
783 | 14ad7326 | pastith | return;
|
784 | 14ad7326 | pastith | } catch (GSSIOException e) {
|
785 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
786 | 14ad7326 | pastith | return;
|
787 | 14ad7326 | pastith | } catch (RpcException e) {
|
788 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
789 | 14ad7326 | pastith | return;
|
790 | 14ad7326 | pastith | } catch (DuplicateNameException e) {
|
791 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_CONFLICT); |
792 | 14ad7326 | pastith | return;
|
793 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
794 | 3b6b7f25 | Dimitris Routsis | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
795 | 3b6b7f25 | Dimitris Routsis | return;
|
796 | 14ad7326 | pastith | } |
797 | 14ad7326 | pastith | |
798 | 14ad7326 | pastith | if (result) {
|
799 | 14ad7326 | pastith | if (exists)
|
800 | 14ad7326 | pastith | resp.setStatus(HttpServletResponse.SC_NO_CONTENT); |
801 | 14ad7326 | pastith | else
|
802 | 14ad7326 | pastith | resp.setStatus(HttpServletResponse.SC_CREATED); |
803 | 14ad7326 | pastith | } else
|
804 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_CONFLICT); |
805 | 14ad7326 | pastith | |
806 | 14ad7326 | pastith | } |
807 | 14ad7326 | pastith | |
808 | 14ad7326 | pastith | @Override
|
809 | 14ad7326 | pastith | protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { |
810 | 14ad7326 | pastith | // Serve the requested resource, without the data content
|
811 | 14ad7326 | pastith | try {
|
812 | 14ad7326 | pastith | serveResource(req, resp, false);
|
813 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
814 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_FOUND); |
815 | 14ad7326 | pastith | return;
|
816 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
817 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_FORBIDDEN); |
818 | 14ad7326 | pastith | return;
|
819 | 14ad7326 | pastith | } catch (RpcException e) {
|
820 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
821 | 14ad7326 | pastith | return;
|
822 | 14ad7326 | pastith | } |
823 | 14ad7326 | pastith | } |
824 | 14ad7326 | pastith | |
825 | 14ad7326 | pastith | /**
|
826 | 14ad7326 | pastith | * The UNLOCK method.
|
827 | 14ad7326 | pastith | *
|
828 | 14ad7326 | pastith | * @param req the HTTP request
|
829 | 14ad7326 | pastith | * @param resp the HTTP response
|
830 | 14ad7326 | pastith | * @throws IOException if an error occurs while sending the response
|
831 | 14ad7326 | pastith | */
|
832 | 92012b4d | droutsis | private void doUnlock(@SuppressWarnings("unused") HttpServletRequest req, HttpServletResponse resp) throws IOException { |
833 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_NO_CONTENT); |
834 | 14ad7326 | pastith | } |
835 | 14ad7326 | pastith | |
836 | 14ad7326 | pastith | /**
|
837 | 14ad7326 | pastith | * The LOCK method.
|
838 | 14ad7326 | pastith | *
|
839 | 14ad7326 | pastith | * @param req the HTTP request
|
840 | 14ad7326 | pastith | * @param resp the HTTP response
|
841 | 14ad7326 | pastith | * @throws IOException if an error occurs while sending the response
|
842 | 14ad7326 | pastith | * @throws ServletException
|
843 | 14ad7326 | pastith | */
|
844 | 14ad7326 | pastith | private void doLock(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { |
845 | 14ad7326 | pastith | LockInfo lock = new LockInfo();
|
846 | 14ad7326 | pastith | // Parsing lock request
|
847 | 14ad7326 | pastith | |
848 | 14ad7326 | pastith | // Parsing depth header
|
849 | 14ad7326 | pastith | String depthStr = req.getHeader("Depth"); |
850 | 14ad7326 | pastith | if (depthStr == null) |
851 | 14ad7326 | pastith | lock.depth = INFINITY; |
852 | 14ad7326 | pastith | else if (depthStr.equals("0")) |
853 | 14ad7326 | pastith | lock.depth = 0;
|
854 | 14ad7326 | pastith | else
|
855 | 14ad7326 | pastith | lock.depth = INFINITY; |
856 | 14ad7326 | pastith | |
857 | 14ad7326 | pastith | // Parsing timeout header
|
858 | 14ad7326 | pastith | int lockDuration = DEFAULT_TIMEOUT;
|
859 | 14ad7326 | pastith | String lockDurationStr = req.getHeader("Timeout"); |
860 | 14ad7326 | pastith | if (lockDurationStr == null) |
861 | 14ad7326 | pastith | lockDuration = DEFAULT_TIMEOUT; |
862 | 14ad7326 | pastith | else {
|
863 | 14ad7326 | pastith | int commaPos = lockDurationStr.indexOf(","); |
864 | 14ad7326 | pastith | // If multiple timeouts, just use the first
|
865 | 14ad7326 | pastith | if (commaPos != -1) |
866 | 14ad7326 | pastith | lockDurationStr = lockDurationStr.substring(0, commaPos);
|
867 | 14ad7326 | pastith | if (lockDurationStr.startsWith("Second-")) |
868 | 14ad7326 | pastith | lockDuration = new Integer(lockDurationStr.substring(7)).intValue(); |
869 | 14ad7326 | pastith | else if (lockDurationStr.equalsIgnoreCase("infinity")) |
870 | 14ad7326 | pastith | lockDuration = MAX_TIMEOUT; |
871 | 14ad7326 | pastith | else
|
872 | 14ad7326 | pastith | try {
|
873 | 14ad7326 | pastith | lockDuration = new Integer(lockDurationStr).intValue(); |
874 | 14ad7326 | pastith | } catch (NumberFormatException e) { |
875 | 14ad7326 | pastith | lockDuration = MAX_TIMEOUT; |
876 | 14ad7326 | pastith | } |
877 | 14ad7326 | pastith | if (lockDuration == 0) |
878 | 14ad7326 | pastith | lockDuration = DEFAULT_TIMEOUT; |
879 | 14ad7326 | pastith | if (lockDuration > MAX_TIMEOUT)
|
880 | 14ad7326 | pastith | lockDuration = MAX_TIMEOUT; |
881 | 14ad7326 | pastith | } |
882 | 14ad7326 | pastith | lock.expiresAt = System.currentTimeMillis() + lockDuration * 1000; |
883 | 14ad7326 | pastith | |
884 | 14ad7326 | pastith | int lockRequestType = LOCK_CREATION;
|
885 | 14ad7326 | pastith | Node lockInfoNode = null;
|
886 | 14ad7326 | pastith | DocumentBuilder documentBuilder = getDocumentBuilder();
|
887 | 14ad7326 | pastith | |
888 | 14ad7326 | pastith | try {
|
889 | 14ad7326 | pastith | Document document = documentBuilder.parse(new InputSource(req.getInputStream())); |
890 | 14ad7326 | pastith | // Get the root element of the document
|
891 | 14ad7326 | pastith | Element rootElement = document.getDocumentElement();
|
892 | 14ad7326 | pastith | lockInfoNode = rootElement; |
893 | 14ad7326 | pastith | } catch (IOException e) { |
894 | 14ad7326 | pastith | lockRequestType = LOCK_REFRESH; |
895 | 14ad7326 | pastith | } catch (SAXException e) {
|
896 | 14ad7326 | pastith | lockRequestType = LOCK_REFRESH; |
897 | 14ad7326 | pastith | } |
898 | 14ad7326 | pastith | |
899 | 14ad7326 | pastith | if (lockInfoNode != null) { |
900 | 14ad7326 | pastith | // Reading lock information
|
901 | 14ad7326 | pastith | NodeList childList = lockInfoNode.getChildNodes(); |
902 | 14ad7326 | pastith | StringWriter strWriter = null; |
903 | 14ad7326 | pastith | DOMWriter domWriter = null;
|
904 | 14ad7326 | pastith | |
905 | 14ad7326 | pastith | Node lockScopeNode = null;
|
906 | 14ad7326 | pastith | Node lockTypeNode = null;
|
907 | 14ad7326 | pastith | Node lockOwnerNode = null;
|
908 | 14ad7326 | pastith | |
909 | 14ad7326 | pastith | for (int i = 0; i < childList.getLength(); i++) { |
910 | 14ad7326 | pastith | Node currentNode = childList.item(i); |
911 | 14ad7326 | pastith | switch (currentNode.getNodeType()) {
|
912 | 14ad7326 | pastith | case Node.TEXT_NODE:
|
913 | 14ad7326 | pastith | break;
|
914 | 14ad7326 | pastith | case Node.ELEMENT_NODE:
|
915 | 14ad7326 | pastith | String nodeName = currentNode.getNodeName();
|
916 | 14ad7326 | pastith | if (nodeName.endsWith("lockscope")) |
917 | 14ad7326 | pastith | lockScopeNode = currentNode; |
918 | 14ad7326 | pastith | if (nodeName.endsWith("locktype")) |
919 | 14ad7326 | pastith | lockTypeNode = currentNode; |
920 | 14ad7326 | pastith | if (nodeName.endsWith("owner")) |
921 | 14ad7326 | pastith | lockOwnerNode = currentNode; |
922 | 14ad7326 | pastith | break;
|
923 | 14ad7326 | pastith | } |
924 | 14ad7326 | pastith | } |
925 | 14ad7326 | pastith | |
926 | 14ad7326 | pastith | if (lockScopeNode != null) { |
927 | 14ad7326 | pastith | childList = lockScopeNode.getChildNodes(); |
928 | 14ad7326 | pastith | for (int i = 0; i < childList.getLength(); i++) { |
929 | 14ad7326 | pastith | Node currentNode = childList.item(i); |
930 | 14ad7326 | pastith | switch (currentNode.getNodeType()) {
|
931 | 14ad7326 | pastith | case Node.TEXT_NODE:
|
932 | 14ad7326 | pastith | break;
|
933 | 14ad7326 | pastith | case Node.ELEMENT_NODE:
|
934 | 14ad7326 | pastith | String tempScope = currentNode.getNodeName();
|
935 | 14ad7326 | pastith | if (tempScope.indexOf(':') != -1) |
936 | 14ad7326 | pastith | lock.scope = tempScope.substring(tempScope.indexOf(':') + 1); |
937 | 14ad7326 | pastith | else
|
938 | 14ad7326 | pastith | lock.scope = tempScope; |
939 | 14ad7326 | pastith | break;
|
940 | 14ad7326 | pastith | } |
941 | 14ad7326 | pastith | } |
942 | 14ad7326 | pastith | if (lock.scope == null) |
943 | 14ad7326 | pastith | // Bad request
|
944 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_BAD_REQUEST); |
945 | 14ad7326 | pastith | } else
|
946 | 14ad7326 | pastith | // Bad request
|
947 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_BAD_REQUEST); |
948 | 14ad7326 | pastith | |
949 | 14ad7326 | pastith | if (lockTypeNode != null) { |
950 | 14ad7326 | pastith | childList = lockTypeNode.getChildNodes(); |
951 | 14ad7326 | pastith | for (int i = 0; i < childList.getLength(); i++) { |
952 | 14ad7326 | pastith | Node currentNode = childList.item(i); |
953 | 14ad7326 | pastith | switch (currentNode.getNodeType()) {
|
954 | 14ad7326 | pastith | case Node.TEXT_NODE:
|
955 | 14ad7326 | pastith | break;
|
956 | 14ad7326 | pastith | case Node.ELEMENT_NODE:
|
957 | 14ad7326 | pastith | String tempType = currentNode.getNodeName();
|
958 | 14ad7326 | pastith | if (tempType.indexOf(':') != -1) |
959 | 14ad7326 | pastith | lock.type = tempType.substring(tempType.indexOf(':') + 1); |
960 | 14ad7326 | pastith | else
|
961 | 14ad7326 | pastith | lock.type = tempType; |
962 | 14ad7326 | pastith | break;
|
963 | 14ad7326 | pastith | } |
964 | 14ad7326 | pastith | } |
965 | 14ad7326 | pastith | |
966 | 14ad7326 | pastith | if (lock.type == null) |
967 | 14ad7326 | pastith | // Bad request
|
968 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_BAD_REQUEST); |
969 | 14ad7326 | pastith | } else
|
970 | 14ad7326 | pastith | // Bad request
|
971 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_BAD_REQUEST); |
972 | 14ad7326 | pastith | |
973 | 14ad7326 | pastith | if (lockOwnerNode != null) { |
974 | 14ad7326 | pastith | childList = lockOwnerNode.getChildNodes(); |
975 | 14ad7326 | pastith | for (int i = 0; i < childList.getLength(); i++) { |
976 | 14ad7326 | pastith | Node currentNode = childList.item(i); |
977 | 14ad7326 | pastith | switch (currentNode.getNodeType()) {
|
978 | 14ad7326 | pastith | case Node.TEXT_NODE:
|
979 | 14ad7326 | pastith | lock.owner += currentNode.getNodeValue(); |
980 | 14ad7326 | pastith | break;
|
981 | 14ad7326 | pastith | case Node.ELEMENT_NODE:
|
982 | 14ad7326 | pastith | strWriter = new StringWriter(); |
983 | 14ad7326 | pastith | domWriter = new DOMWriter(strWriter, true); |
984 | 14ad7326 | pastith | domWriter.setQualifiedNames(false);
|
985 | 14ad7326 | pastith | domWriter.print(currentNode); |
986 | 14ad7326 | pastith | lock.owner += strWriter.toString(); |
987 | 14ad7326 | pastith | break;
|
988 | 14ad7326 | pastith | } |
989 | 14ad7326 | pastith | } |
990 | 14ad7326 | pastith | |
991 | 14ad7326 | pastith | if (lock.owner == null) |
992 | 14ad7326 | pastith | // Bad request
|
993 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_BAD_REQUEST); |
994 | 14ad7326 | pastith | } else
|
995 | 14ad7326 | pastith | lock.owner = new String(); |
996 | 14ad7326 | pastith | } |
997 | 14ad7326 | pastith | |
998 | 14ad7326 | pastith | String path = getRelativePath(req);
|
999 | 14ad7326 | pastith | lock.path = path; |
1000 | 14ad7326 | pastith | User user = getUser(req); |
1001 | 14ad7326 | pastith | boolean exists = true; |
1002 | 14ad7326 | pastith | Object object = null; |
1003 | 14ad7326 | pastith | try {
|
1004 | 68410d59 | pastith | object = getService().getResourceAtPath(user.getId(), path, true);
|
1005 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
1006 | 14ad7326 | pastith | exists = false;
|
1007 | 14ad7326 | pastith | } catch (RpcException e) {
|
1008 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1009 | 14ad7326 | pastith | return;
|
1010 | 14ad7326 | pastith | } |
1011 | 14ad7326 | pastith | |
1012 | 14ad7326 | pastith | if (lockRequestType == LOCK_CREATION) {
|
1013 | 14ad7326 | pastith | // Generating lock id
|
1014 | 14ad7326 | pastith | String lockTokenStr = req.getServletPath() + "-" + lock.type + "-" + lock.scope + "-" + req.getUserPrincipal() + "-" + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-" + lock.expiresAt + "-" + System.currentTimeMillis() + "-" + secret; |
1015 | 14ad7326 | pastith | String lockToken = md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));
|
1016 | 14ad7326 | pastith | |
1017 | f7c44c33 | fstamatelopoulos | if (exists && object instanceof Folder && lock.depth == INFINITY) |
1018 | 14ad7326 | pastith | // Locking a collection (and all its member resources)
|
1019 | 92012b4d | droutsis | lock.tokens.addElement(lockToken); |
1020 | 92012b4d | droutsis | else {
|
1021 | 14ad7326 | pastith | // Locking a single resource
|
1022 | 92012b4d | droutsis | lock.tokens.addElement(lockToken); |
1023 | 92012b4d | droutsis | // Add the Lock-Token header as by RFC 2518 8.10.1
|
1024 | 92012b4d | droutsis | // - only do this for newly created locks
|
1025 | 92012b4d | droutsis | resp.addHeader("Lock-Token", "<opaquelocktoken:" + lockToken + ">"); |
1026 | 14ad7326 | pastith | |
1027 | 14ad7326 | pastith | } |
1028 | 14ad7326 | pastith | } |
1029 | 14ad7326 | pastith | |
1030 | 14ad7326 | pastith | if (lockRequestType == LOCK_REFRESH) {
|
1031 | 92012b4d | droutsis | |
1032 | 14ad7326 | pastith | } |
1033 | 92012b4d | droutsis | |
1034 | 14ad7326 | pastith | // Set the status, then generate the XML response containing
|
1035 | 14ad7326 | pastith | // the lock information.
|
1036 | 14ad7326 | pastith | XMLWriter generatedXML = new XMLWriter();
|
1037 | 14ad7326 | pastith | generatedXML.writeXMLHeader(); |
1038 | 14ad7326 | pastith | generatedXML.writeElement(null, "prop" + generateNamespaceDeclarations(), XMLWriter.OPENING); |
1039 | 14ad7326 | pastith | generatedXML.writeElement(null, "lockdiscovery", XMLWriter.OPENING); |
1040 | 14ad7326 | pastith | lock.toXML(generatedXML); |
1041 | 14ad7326 | pastith | generatedXML.writeElement(null, "lockdiscovery", XMLWriter.CLOSING); |
1042 | 14ad7326 | pastith | generatedXML.writeElement(null, "prop", XMLWriter.CLOSING); |
1043 | 14ad7326 | pastith | |
1044 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_OK); |
1045 | 14ad7326 | pastith | resp.setContentType("text/xml; charset=UTF-8");
|
1046 | 14ad7326 | pastith | Writer writer = resp.getWriter();
|
1047 | 14ad7326 | pastith | writer.write(generatedXML.toString()); |
1048 | 14ad7326 | pastith | writer.close(); |
1049 | 14ad7326 | pastith | } |
1050 | 14ad7326 | pastith | |
1051 | 14ad7326 | pastith | /**
|
1052 | 14ad7326 | pastith | * The MOVE method.
|
1053 | 14ad7326 | pastith | *
|
1054 | 14ad7326 | pastith | * @param req the HTTP request
|
1055 | 14ad7326 | pastith | * @param resp the HTTP response
|
1056 | 14ad7326 | pastith | * @throws IOException if an error occurs while sending the response
|
1057 | 14ad7326 | pastith | * @throws ServletException
|
1058 | 14ad7326 | pastith | */
|
1059 | 14ad7326 | pastith | private void doMove(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { |
1060 | 14ad7326 | pastith | if (isLocked(req)) {
|
1061 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_LOCKED); |
1062 | 14ad7326 | pastith | return;
|
1063 | 14ad7326 | pastith | } |
1064 | 14ad7326 | pastith | |
1065 | 14ad7326 | pastith | String path = getRelativePath(req);
|
1066 | 14ad7326 | pastith | |
1067 | 14ad7326 | pastith | if (copyResource(req, resp))
|
1068 | 14ad7326 | pastith | deleteResource(path, req, resp, false);
|
1069 | 14ad7326 | pastith | } |
1070 | 14ad7326 | pastith | |
1071 | 14ad7326 | pastith | /**
|
1072 | 14ad7326 | pastith | * The COPY method.
|
1073 | 14ad7326 | pastith | *
|
1074 | 14ad7326 | pastith | * @param req the HTTP request
|
1075 | 14ad7326 | pastith | * @param resp the HTTP response
|
1076 | 14ad7326 | pastith | * @throws IOException if an error occurs while sending the response
|
1077 | 14ad7326 | pastith | * @throws ServletException
|
1078 | 14ad7326 | pastith | */
|
1079 | 14ad7326 | pastith | private void doCopy(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { |
1080 | 14ad7326 | pastith | copyResource(req, resp); |
1081 | 14ad7326 | pastith | } |
1082 | 14ad7326 | pastith | |
1083 | 14ad7326 | pastith | /**
|
1084 | 14ad7326 | pastith | * The MKCOL method.
|
1085 | 14ad7326 | pastith | *
|
1086 | 14ad7326 | pastith | * @param req the HTTP request
|
1087 | 14ad7326 | pastith | * @param resp the HTTP response
|
1088 | 14ad7326 | pastith | * @throws IOException if an error occurs while sending the response
|
1089 | 14ad7326 | pastith | * @throws ServletException
|
1090 | 14ad7326 | pastith | */
|
1091 | 14ad7326 | pastith | private void doMkcol(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { |
1092 | 14ad7326 | pastith | if (isLocked(req)) {
|
1093 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_LOCKED); |
1094 | 14ad7326 | pastith | return;
|
1095 | 14ad7326 | pastith | } |
1096 | 3b6b7f25 | Dimitris Routsis | final String path = getRelativePath(req); |
1097 | 14ad7326 | pastith | if (path.toUpperCase().startsWith("/WEB-INF") || path.toUpperCase().startsWith("/META-INF")) { |
1098 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN); |
1099 | 14ad7326 | pastith | return;
|
1100 | 14ad7326 | pastith | } |
1101 | 14ad7326 | pastith | |
1102 | 3b6b7f25 | Dimitris Routsis | final User user = getUser(req);
|
1103 | 14ad7326 | pastith | boolean exists = true; |
1104 | 14ad7326 | pastith | try {
|
1105 | 68410d59 | pastith | getService().getResourceAtPath(user.getId(), path, true);
|
1106 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
1107 | 14ad7326 | pastith | exists = false;
|
1108 | 14ad7326 | pastith | } catch (RpcException e) {
|
1109 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1110 | 14ad7326 | pastith | return;
|
1111 | 14ad7326 | pastith | } |
1112 | 14ad7326 | pastith | |
1113 | 14ad7326 | pastith | // Can't create a collection if a resource already exists at the given
|
1114 | 14ad7326 | pastith | // path.
|
1115 | 14ad7326 | pastith | if (exists) {
|
1116 | 14ad7326 | pastith | // Get allowed methods.
|
1117 | 14ad7326 | pastith | StringBuffer methodsAllowed;
|
1118 | 14ad7326 | pastith | try {
|
1119 | 14ad7326 | pastith | methodsAllowed = determineMethodsAllowed(req); |
1120 | 14ad7326 | pastith | } catch (RpcException e) {
|
1121 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1122 | 14ad7326 | pastith | return;
|
1123 | 14ad7326 | pastith | } |
1124 | 14ad7326 | pastith | resp.addHeader("Allow", methodsAllowed.toString());
|
1125 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); |
1126 | 14ad7326 | pastith | return;
|
1127 | 14ad7326 | pastith | } |
1128 | 14ad7326 | pastith | |
1129 | 14ad7326 | pastith | if (req.getInputStream().available() > 0) { |
1130 | 14ad7326 | pastith | DocumentBuilder documentBuilder = getDocumentBuilder();
|
1131 | 14ad7326 | pastith | try {
|
1132 | 86c951b2 | Panagiotis Astithas | @SuppressWarnings("unused") |
1133 | 14ad7326 | pastith | Document document = documentBuilder.parse(new InputSource(req.getInputStream())); |
1134 | 14ad7326 | pastith | // TODO : Process this request body
|
1135 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED); |
1136 | 14ad7326 | pastith | return;
|
1137 | 14ad7326 | pastith | } catch (SAXException saxe) {
|
1138 | 14ad7326 | pastith | // Parse error - assume invalid content
|
1139 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_BAD_REQUEST); |
1140 | 14ad7326 | pastith | return;
|
1141 | 14ad7326 | pastith | } |
1142 | 14ad7326 | pastith | } |
1143 | 14ad7326 | pastith | |
1144 | 14ad7326 | pastith | Object parent;
|
1145 | 14ad7326 | pastith | try {
|
1146 | 68410d59 | pastith | parent = getService().getResourceAtPath(user.getId(), getParentPath(path), true);
|
1147 | a3b6a99f | Panagiotis Astithas | } catch (ObjectNotFoundException e) {
|
1148 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_CONFLICT, WebdavStatus.getStatusText(WebdavStatus.SC_CONFLICT)); |
1149 | 14ad7326 | pastith | return;
|
1150 | a3b6a99f | Panagiotis Astithas | } catch (RpcException e) {
|
1151 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1152 | 14ad7326 | pastith | return;
|
1153 | 14ad7326 | pastith | } |
1154 | 14ad7326 | pastith | try {
|
1155 | f7c44c33 | fstamatelopoulos | if (parent instanceof Folder) { |
1156 | f7c44c33 | fstamatelopoulos | final Folder folderLocal = (Folder) parent;
|
1157 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
1158 | 3b6b7f25 | Dimitris Routsis | @Override
|
1159 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
1160 | f7c44c33 | fstamatelopoulos | getService().createFolder(user.getId(), folderLocal.getId(), getLastElement(path)); |
1161 | 3b6b7f25 | Dimitris Routsis | return null; |
1162 | 3b6b7f25 | Dimitris Routsis | } |
1163 | 3b6b7f25 | Dimitris Routsis | }); |
1164 | 14ad7326 | pastith | } else {
|
1165 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN, WebdavStatus.getStatusText(WebdavStatus.SC_FORBIDDEN)); |
1166 | 14ad7326 | pastith | return;
|
1167 | 14ad7326 | pastith | } |
1168 | 14ad7326 | pastith | } catch (DuplicateNameException e) {
|
1169 | 14ad7326 | pastith | // XXX If the existing name is a folder we should be returning
|
1170 | 14ad7326 | pastith | // SC_METHOD_NOT_ALLOWED, or even better, just do the createFolder
|
1171 | 14ad7326 | pastith | // without checking first and then deal with the exceptions.
|
1172 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN, WebdavStatus.getStatusText(WebdavStatus.SC_FORBIDDEN)); |
1173 | 14ad7326 | pastith | return;
|
1174 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
1175 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN, WebdavStatus.getStatusText(WebdavStatus.SC_FORBIDDEN)); |
1176 | 14ad7326 | pastith | return;
|
1177 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
1178 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_CONFLICT, WebdavStatus.getStatusText(WebdavStatus.SC_CONFLICT)); |
1179 | 14ad7326 | pastith | return;
|
1180 | 14ad7326 | pastith | } catch (RpcException e) {
|
1181 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1182 | 14ad7326 | pastith | return;
|
1183 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
1184 | 3b6b7f25 | Dimitris Routsis | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1185 | 3b6b7f25 | Dimitris Routsis | return;
|
1186 | 14ad7326 | pastith | } |
1187 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_CREATED); |
1188 | 14ad7326 | pastith | } |
1189 | 14ad7326 | pastith | |
1190 | 14ad7326 | pastith | /**
|
1191 | 14ad7326 | pastith | * For a provided path, remove the last element and return the rest, that is
|
1192 | 14ad7326 | pastith | * the path of the parent folder.
|
1193 | 14ad7326 | pastith | *
|
1194 | 14ad7326 | pastith | * @param path the specified path
|
1195 | 14ad7326 | pastith | * @return the path of the parent folder
|
1196 | 14ad7326 | pastith | * @throws ObjectNotFoundException if the provided string contains no path
|
1197 | 14ad7326 | pastith | * delimiters
|
1198 | 14ad7326 | pastith | */
|
1199 | 14ad7326 | pastith | protected String getParentPath(String path) throws ObjectNotFoundException { |
1200 | 14ad7326 | pastith | int lastDelimiter = path.lastIndexOf('/'); |
1201 | 14ad7326 | pastith | if (lastDelimiter == 0) |
1202 | 14ad7326 | pastith | return "/"; |
1203 | 14ad7326 | pastith | if (lastDelimiter == -1) |
1204 | 14ad7326 | pastith | // No path found.
|
1205 | 14ad7326 | pastith | throw new ObjectNotFoundException("There is no parent in the path: " + path); |
1206 | 14ad7326 | pastith | else if (lastDelimiter < path.length() - 1) |
1207 | 14ad7326 | pastith | // Return the part before the delimiter.
|
1208 | 14ad7326 | pastith | return path.substring(0, lastDelimiter); |
1209 | 14ad7326 | pastith | else {
|
1210 | 14ad7326 | pastith | // Remove the trailing delimiter and then recurse.
|
1211 | 14ad7326 | pastith | String strippedTrail = path.substring(0, lastDelimiter); |
1212 | 14ad7326 | pastith | return getParentPath(strippedTrail);
|
1213 | 14ad7326 | pastith | } |
1214 | 14ad7326 | pastith | } |
1215 | 14ad7326 | pastith | |
1216 | 14ad7326 | pastith | /**
|
1217 | 14ad7326 | pastith | * Get the last element in a path that denotes the file or folder name.
|
1218 | 14ad7326 | pastith | *
|
1219 | 14ad7326 | pastith | * @param path the provided path
|
1220 | 14ad7326 | pastith | * @return the last element in the path
|
1221 | 14ad7326 | pastith | */
|
1222 | 14ad7326 | pastith | protected String getLastElement(String path) { |
1223 | 14ad7326 | pastith | int lastDelimiter = path.lastIndexOf('/'); |
1224 | 14ad7326 | pastith | if (lastDelimiter == -1) |
1225 | 14ad7326 | pastith | // No path found.
|
1226 | 14ad7326 | pastith | return path;
|
1227 | 14ad7326 | pastith | else if (lastDelimiter < path.length() - 1) |
1228 | 14ad7326 | pastith | // Return the part after the delimiter.
|
1229 | 14ad7326 | pastith | return path.substring(lastDelimiter + 1); |
1230 | 14ad7326 | pastith | else {
|
1231 | 14ad7326 | pastith | // Remove the trailing delimiter and then recurse.
|
1232 | 14ad7326 | pastith | String strippedTrail = path.substring(0, lastDelimiter); |
1233 | 14ad7326 | pastith | return getLastElement(strippedTrail);
|
1234 | 14ad7326 | pastith | } |
1235 | 14ad7326 | pastith | } |
1236 | 14ad7326 | pastith | |
1237 | 14ad7326 | pastith | /**
|
1238 | 14ad7326 | pastith | * Only use the PathInfo for determining the requested path. If the
|
1239 | 14ad7326 | pastith | * ServletPath is non-null, it will be because the WebDAV servlet has been
|
1240 | 14ad7326 | pastith | * mapped to a URL other than /* to configure editing at different URL than
|
1241 | 14ad7326 | pastith | * normal viewing.
|
1242 | 14ad7326 | pastith | *
|
1243 | 14ad7326 | pastith | * @param request the servlet request we are processing
|
1244 | 14ad7326 | pastith | * @return the relative path
|
1245 | b36b0a98 | droutsis | * @throws UnsupportedEncodingException
|
1246 | 14ad7326 | pastith | */
|
1247 | 14ad7326 | pastith | protected String getRelativePath(HttpServletRequest request) { |
1248 | b36b0a98 | droutsis | // Remove the servlet path from the request URI.
|
1249 | b36b0a98 | droutsis | String p = request.getRequestURI();
|
1250 | b36b0a98 | droutsis | String servletPath = request.getContextPath() + request.getServletPath();
|
1251 | b36b0a98 | droutsis | String result = p.substring(servletPath.length());
|
1252 | b36b0a98 | droutsis | try {
|
1253 | b36b0a98 | droutsis | result = URLDecoder.decode(result, "UTF-8"); |
1254 | b36b0a98 | droutsis | } catch (UnsupportedEncodingException e) { |
1255 | b36b0a98 | droutsis | } |
1256 | 14ad7326 | pastith | if (result == null || result.equals("")) |
1257 | 14ad7326 | pastith | result = "/";
|
1258 | 14ad7326 | pastith | return result;
|
1259 | 14ad7326 | pastith | |
1260 | 14ad7326 | pastith | } |
1261 | 14ad7326 | pastith | |
1262 | 14ad7326 | pastith | /**
|
1263 | 14ad7326 | pastith | * Return JAXP document builder instance.
|
1264 | 14ad7326 | pastith | *
|
1265 | 14ad7326 | pastith | * @return the DocumentBuilder
|
1266 | 14ad7326 | pastith | * @throws ServletException
|
1267 | 14ad7326 | pastith | */
|
1268 | 14ad7326 | pastith | private DocumentBuilder getDocumentBuilder() throws ServletException { |
1269 | 14ad7326 | pastith | DocumentBuilder documentBuilder = null; |
1270 | 14ad7326 | pastith | DocumentBuilderFactory documentBuilderFactory = null; |
1271 | 14ad7326 | pastith | try {
|
1272 | 14ad7326 | pastith | documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
1273 | 14ad7326 | pastith | documentBuilderFactory.setNamespaceAware(true);
|
1274 | 14ad7326 | pastith | documentBuilderFactory.setExpandEntityReferences(false);
|
1275 | 14ad7326 | pastith | documentBuilder = documentBuilderFactory.newDocumentBuilder(); |
1276 | 14ad7326 | pastith | documentBuilder.setEntityResolver(new WebdavResolver(getServletContext()));
|
1277 | 14ad7326 | pastith | } catch (ParserConfigurationException e) { |
1278 | 14ad7326 | pastith | throw new ServletException("Error while creating a document builder"); |
1279 | 14ad7326 | pastith | } |
1280 | 14ad7326 | pastith | return documentBuilder;
|
1281 | 14ad7326 | pastith | } |
1282 | 14ad7326 | pastith | |
1283 | 14ad7326 | pastith | /**
|
1284 | 14ad7326 | pastith | * Generate the namespace declarations.
|
1285 | 14ad7326 | pastith | *
|
1286 | 14ad7326 | pastith | * @return the namespace declarations
|
1287 | 14ad7326 | pastith | */
|
1288 | 14ad7326 | pastith | private String generateNamespaceDeclarations() { |
1289 | a3b6a99f | Panagiotis Astithas | return " xmlns:D=\"" + DEFAULT_NAMESPACE + "\""; |
1290 | 14ad7326 | pastith | } |
1291 | 14ad7326 | pastith | |
1292 | 14ad7326 | pastith | /**
|
1293 | 14ad7326 | pastith | * Propfind helper method. Dispays the properties of a lock-null resource.
|
1294 | 14ad7326 | pastith | *
|
1295 | 14ad7326 | pastith | * @param req the HTTP request
|
1296 | 14ad7326 | pastith | * @param resources Resources object associated with this context
|
1297 | 14ad7326 | pastith | * @param generatedXML XML response to the Propfind request
|
1298 | 14ad7326 | pastith | * @param path Path of the current resource
|
1299 | 14ad7326 | pastith | * @param type Propfind type
|
1300 | 14ad7326 | pastith | * @param propertiesVector If the propfind type is find properties by name,
|
1301 | 14ad7326 | pastith | * then this Vector contains those properties
|
1302 | 14ad7326 | pastith | */
|
1303 | 92012b4d | droutsis | @SuppressWarnings("unused") |
1304 | 14ad7326 | pastith | private void parseLockNullProperties(HttpServletRequest req, XMLWriter generatedXML, String path, int type, Vector propertiesVector) { |
1305 | 92012b4d | droutsis | return;
|
1306 | 14ad7326 | pastith | } |
1307 | 14ad7326 | pastith | |
1308 | 14ad7326 | pastith | /**
|
1309 | 14ad7326 | pastith | * Propfind helper method.
|
1310 | 14ad7326 | pastith | *
|
1311 | 14ad7326 | pastith | * @param req The servlet request
|
1312 | 14ad7326 | pastith | * @param resources Resources object associated with this context
|
1313 | 14ad7326 | pastith | * @param generatedXML XML response to the Propfind request
|
1314 | 14ad7326 | pastith | * @param path Path of the current resource
|
1315 | 14ad7326 | pastith | * @param type Propfind type
|
1316 | 14ad7326 | pastith | * @param propertiesVector If the propfind type is find properties by name,
|
1317 | 14ad7326 | pastith | * then this Vector contains those properties
|
1318 | 14ad7326 | pastith | * @param resource the resource object
|
1319 | 14ad7326 | pastith | */
|
1320 | 14ad7326 | pastith | private void parseProperties(HttpServletRequest req, XMLWriter generatedXML, String path, int type, Vector<String> propertiesVector, Object resource) { |
1321 | 14ad7326 | pastith | |
1322 | 14ad7326 | pastith | // Exclude any resource in the /WEB-INF and /META-INF subdirectories
|
1323 | 14ad7326 | pastith | // (the "toUpperCase()" avoids problems on Windows systems)
|
1324 | 14ad7326 | pastith | if (path.toUpperCase().startsWith("/WEB-INF") || path.toUpperCase().startsWith("/META-INF")) |
1325 | 14ad7326 | pastith | return;
|
1326 | 14ad7326 | pastith | |
1327 | f7c44c33 | fstamatelopoulos | Folder folderLocal = null;
|
1328 | f7c44c33 | fstamatelopoulos | FileHeader fileLocal = null;
|
1329 | f7c44c33 | fstamatelopoulos | if (resource instanceof Folder) |
1330 | f7c44c33 | fstamatelopoulos | folderLocal = (Folder) resource; |
1331 | 14ad7326 | pastith | else
|
1332 | f7c44c33 | fstamatelopoulos | fileLocal = (FileHeader) resource; |
1333 | 14ad7326 | pastith | // Retrieve the creation date.
|
1334 | 14ad7326 | pastith | long creation = 0; |
1335 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1336 | f7c44c33 | fstamatelopoulos | creation = folderLocal.getAuditInfo().getCreationDate().getTime(); |
1337 | 14ad7326 | pastith | else
|
1338 | f7c44c33 | fstamatelopoulos | creation = fileLocal.getAuditInfo().getCreationDate().getTime(); |
1339 | 14ad7326 | pastith | // Retrieve the modification date.
|
1340 | 14ad7326 | pastith | long modification = 0; |
1341 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1342 | f7c44c33 | fstamatelopoulos | modification = folderLocal.getAuditInfo().getCreationDate().getTime(); |
1343 | 14ad7326 | pastith | else
|
1344 | f7c44c33 | fstamatelopoulos | modification = fileLocal.getAuditInfo().getCreationDate().getTime(); |
1345 | 14ad7326 | pastith | |
1346 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:response", XMLWriter.OPENING); |
1347 | 14ad7326 | pastith | String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK)); |
1348 | 14ad7326 | pastith | |
1349 | 14ad7326 | pastith | // Generating href element
|
1350 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:href", XMLWriter.OPENING); |
1351 | 14ad7326 | pastith | |
1352 | 14ad7326 | pastith | String href = req.getContextPath() + req.getServletPath();
|
1353 | 14ad7326 | pastith | if (href.endsWith("/") && path.startsWith("/")) |
1354 | 14ad7326 | pastith | href += path.substring(1);
|
1355 | 14ad7326 | pastith | else
|
1356 | 14ad7326 | pastith | href += path; |
1357 | f7c44c33 | fstamatelopoulos | if (folderLocal != null && !href.endsWith("/")) |
1358 | 14ad7326 | pastith | href += "/";
|
1359 | 14ad7326 | pastith | |
1360 | 14ad7326 | pastith | generatedXML.writeText(rewriteUrl(href)); |
1361 | 14ad7326 | pastith | |
1362 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:href", XMLWriter.CLOSING); |
1363 | 14ad7326 | pastith | |
1364 | 14ad7326 | pastith | String resourceName = path;
|
1365 | 14ad7326 | pastith | int lastSlash = path.lastIndexOf('/'); |
1366 | 14ad7326 | pastith | if (lastSlash != -1) |
1367 | 14ad7326 | pastith | resourceName = resourceName.substring(lastSlash + 1);
|
1368 | a3b6a99f | Panagiotis Astithas | if (resourceName.isEmpty())
|
1369 | a3b6a99f | Panagiotis Astithas | resourceName = "/";
|
1370 | 14ad7326 | pastith | |
1371 | 14ad7326 | pastith | switch (type) {
|
1372 | 14ad7326 | pastith | |
1373 | 14ad7326 | pastith | case FIND_ALL_PROP:
|
1374 | 14ad7326 | pastith | |
1375 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.OPENING); |
1376 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.OPENING); |
1377 | 14ad7326 | pastith | |
1378 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:creationdate", getISOCreationDate(creation)); |
1379 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:displayname", XMLWriter.OPENING); |
1380 | 14ad7326 | pastith | generatedXML.writeData(resourceName); |
1381 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:displayname", XMLWriter.CLOSING); |
1382 | f7c44c33 | fstamatelopoulos | if (fileLocal != null) { |
1383 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:getlastmodified", FastHttpDateFormat.formatDate(modification, null)); |
1384 | f7c44c33 | fstamatelopoulos | generatedXML.writeProperty(null, "D:getcontentlength", String.valueOf(fileLocal.getCurrentBody().getFileSize())); |
1385 | f7c44c33 | fstamatelopoulos | String contentType = fileLocal.getCurrentBody().getMimeType();
|
1386 | 14ad7326 | pastith | if (contentType != null) |
1387 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:getcontenttype", contentType); |
1388 | f7c44c33 | fstamatelopoulos | generatedXML.writeProperty(null, "D:getetag", getETag(fileLocal, null)); |
1389 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.NO_CONTENT); |
1390 | 14ad7326 | pastith | } else {
|
1391 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.OPENING); |
1392 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:collection", XMLWriter.NO_CONTENT); |
1393 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.CLOSING); |
1394 | 14ad7326 | pastith | } |
1395 | 14ad7326 | pastith | |
1396 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:source", ""); |
1397 | 14ad7326 | pastith | |
1398 | a3b6a99f | Panagiotis Astithas | String supportedLocks = "<D:lockentry>" + "<D:lockscope><D:exclusive/></D:lockscope>" + "<D:locktype><D:write/></D:locktype>" + "</D:lockentry>" + "<D:lockentry>" + "<D:lockscope><D:shared/></D:lockscope>" + "<D:locktype><D:write/></D:locktype>" + "</D:lockentry>"; |
1399 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:supportedlock", XMLWriter.OPENING); |
1400 | 14ad7326 | pastith | generatedXML.writeText(supportedLocks); |
1401 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:supportedlock", XMLWriter.CLOSING); |
1402 | 14ad7326 | pastith | |
1403 | 14ad7326 | pastith | generateLockDiscovery(path, generatedXML); |
1404 | 14ad7326 | pastith | |
1405 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.CLOSING); |
1406 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.OPENING); |
1407 | 14ad7326 | pastith | generatedXML.writeText(status); |
1408 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.CLOSING); |
1409 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.CLOSING); |
1410 | 14ad7326 | pastith | |
1411 | 14ad7326 | pastith | break;
|
1412 | 14ad7326 | pastith | |
1413 | 14ad7326 | pastith | case FIND_PROPERTY_NAMES:
|
1414 | 14ad7326 | pastith | |
1415 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.OPENING); |
1416 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.OPENING); |
1417 | 14ad7326 | pastith | |
1418 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:creationdate", XMLWriter.NO_CONTENT); |
1419 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:displayname", XMLWriter.NO_CONTENT); |
1420 | f7c44c33 | fstamatelopoulos | if (fileLocal != null) { |
1421 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:getcontentlanguage", XMLWriter.NO_CONTENT); |
1422 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:getcontentlength", XMLWriter.NO_CONTENT); |
1423 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:getcontenttype", XMLWriter.NO_CONTENT); |
1424 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:getetag", XMLWriter.NO_CONTENT); |
1425 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:getlastmodified", XMLWriter.NO_CONTENT); |
1426 | 14ad7326 | pastith | } |
1427 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.NO_CONTENT); |
1428 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:source", XMLWriter.NO_CONTENT); |
1429 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:lockdiscovery", XMLWriter.NO_CONTENT); |
1430 | 14ad7326 | pastith | |
1431 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.CLOSING); |
1432 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.OPENING); |
1433 | 14ad7326 | pastith | generatedXML.writeText(status); |
1434 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.CLOSING); |
1435 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.CLOSING); |
1436 | 14ad7326 | pastith | |
1437 | 14ad7326 | pastith | break;
|
1438 | 14ad7326 | pastith | |
1439 | 14ad7326 | pastith | case FIND_BY_PROPERTY:
|
1440 | 14ad7326 | pastith | |
1441 | 14ad7326 | pastith | Vector<String> propertiesNotFound = new Vector<String>(); |
1442 | 14ad7326 | pastith | |
1443 | 14ad7326 | pastith | // Parse the list of properties
|
1444 | 14ad7326 | pastith | |
1445 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.OPENING); |
1446 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.OPENING); |
1447 | 14ad7326 | pastith | |
1448 | 14ad7326 | pastith | Enumeration<String> properties = propertiesVector.elements(); |
1449 | 14ad7326 | pastith | |
1450 | 14ad7326 | pastith | while (properties.hasMoreElements()) {
|
1451 | 14ad7326 | pastith | |
1452 | 14ad7326 | pastith | String property = properties.nextElement();
|
1453 | 14ad7326 | pastith | |
1454 | a3b6a99f | Panagiotis Astithas | if (property.equals("D:creationdate")) |
1455 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:creationdate", getISOCreationDate(creation)); |
1456 | a3b6a99f | Panagiotis Astithas | else if (property.equals("D:displayname")) { |
1457 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:displayname", XMLWriter.OPENING); |
1458 | 14ad7326 | pastith | generatedXML.writeData(resourceName); |
1459 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:displayname", XMLWriter.CLOSING); |
1460 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:getcontentlanguage")) { |
1461 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1462 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1463 | 14ad7326 | pastith | else
|
1464 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:getcontentlanguage", XMLWriter.NO_CONTENT); |
1465 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:getcontentlength")) { |
1466 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1467 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1468 | 14ad7326 | pastith | else
|
1469 | f7c44c33 | fstamatelopoulos | generatedXML.writeProperty(null, "D:getcontentlength", String.valueOf(fileLocal.getCurrentBody().getFileSize())); |
1470 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:getcontenttype")) { |
1471 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1472 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1473 | 14ad7326 | pastith | else
|
1474 | 14ad7326 | pastith | // XXX Once we properly store the MIME type in the
|
1475 | 14ad7326 | pastith | // file,
|
1476 | 14ad7326 | pastith | // retrieve it from there.
|
1477 | f7c44c33 | fstamatelopoulos | generatedXML.writeProperty(null, "D:getcontenttype", getServletContext().getMimeType(fileLocal.getName())); |
1478 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:getetag")) { |
1479 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1480 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1481 | 14ad7326 | pastith | else
|
1482 | f7c44c33 | fstamatelopoulos | generatedXML.writeProperty(null, "D:getetag", getETag(fileLocal, null)); |
1483 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:getlastmodified")) { |
1484 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) |
1485 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1486 | 14ad7326 | pastith | else
|
1487 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:getlastmodified", FastHttpDateFormat.formatDate(modification, null)); |
1488 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:resourcetype")) { |
1489 | f7c44c33 | fstamatelopoulos | if (folderLocal != null) { |
1490 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.OPENING); |
1491 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:collection", XMLWriter.NO_CONTENT); |
1492 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.CLOSING); |
1493 | 14ad7326 | pastith | } else
|
1494 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:resourcetype", XMLWriter.NO_CONTENT); |
1495 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:source")) |
1496 | a3b6a99f | Panagiotis Astithas | generatedXML.writeProperty(null, "D:source", ""); |
1497 | a3b6a99f | Panagiotis Astithas | else if (property.equals("D:supportedlock")) { |
1498 | a3b6a99f | Panagiotis Astithas | supportedLocks = "<D:lockentry>" + "<D:lockscope><D:exclusive/></D:lockscope>" + "<D:locktype><D:write/></D:locktype>" + "</D:lockentry>" + "<D:lockentry>" + "<D:lockscope><D:shared/></D:lockscope>" + "<D:locktype><D:write/></D:locktype>" + "</D:lockentry>"; |
1499 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:supportedlock", XMLWriter.OPENING); |
1500 | 14ad7326 | pastith | generatedXML.writeText(supportedLocks); |
1501 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:supportedlock", XMLWriter.CLOSING); |
1502 | a3b6a99f | Panagiotis Astithas | } else if (property.equals("D:lockdiscovery")) { |
1503 | 14ad7326 | pastith | if (!generateLockDiscovery(path, generatedXML))
|
1504 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1505 | 14ad7326 | pastith | } else
|
1506 | 14ad7326 | pastith | propertiesNotFound.addElement(property); |
1507 | 14ad7326 | pastith | } |
1508 | 14ad7326 | pastith | |
1509 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.CLOSING); |
1510 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.OPENING); |
1511 | 14ad7326 | pastith | generatedXML.writeText(status); |
1512 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.CLOSING); |
1513 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.CLOSING); |
1514 | 14ad7326 | pastith | |
1515 | 14ad7326 | pastith | Enumeration propertiesNotFoundList = propertiesNotFound.elements();
|
1516 | 14ad7326 | pastith | |
1517 | 14ad7326 | pastith | if (propertiesNotFoundList.hasMoreElements()) {
|
1518 | 14ad7326 | pastith | |
1519 | 14ad7326 | pastith | status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " " + WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND)); |
1520 | 14ad7326 | pastith | |
1521 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.OPENING); |
1522 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.OPENING); |
1523 | 14ad7326 | pastith | |
1524 | 14ad7326 | pastith | while (propertiesNotFoundList.hasMoreElements())
|
1525 | 14ad7326 | pastith | generatedXML.writeElement(null, (String) propertiesNotFoundList.nextElement(), XMLWriter.NO_CONTENT); |
1526 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:prop", XMLWriter.CLOSING); |
1527 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.OPENING); |
1528 | 14ad7326 | pastith | generatedXML.writeText(status); |
1529 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:status", XMLWriter.CLOSING); |
1530 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:propstat", XMLWriter.CLOSING); |
1531 | 14ad7326 | pastith | } |
1532 | 14ad7326 | pastith | |
1533 | 14ad7326 | pastith | break;
|
1534 | 14ad7326 | pastith | |
1535 | 14ad7326 | pastith | } |
1536 | 14ad7326 | pastith | |
1537 | a3b6a99f | Panagiotis Astithas | generatedXML.writeElement(null, "D:response", XMLWriter.CLOSING); |
1538 | 14ad7326 | pastith | |
1539 | 14ad7326 | pastith | } |
1540 | 14ad7326 | pastith | |
1541 | 14ad7326 | pastith | /**
|
1542 | 14ad7326 | pastith | * Get the ETag associated with a file.
|
1543 | 14ad7326 | pastith | *
|
1544 | f7c44c33 | fstamatelopoulos | * @param file the FileHeader object for this file
|
1545 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
1546 | 14ad7326 | pastith | * @return a string containing the ETag
|
1547 | 14ad7326 | pastith | */
|
1548 | f7c44c33 | fstamatelopoulos | protected String getETag(FileHeader file, FileBody oldBody) { |
1549 | 14ad7326 | pastith | if (oldBody == null) |
1550 | f7c44c33 | fstamatelopoulos | return "\"" + file.getCurrentBody().getFileSize() + "-" + file.getAuditInfo().getModificationDate().getTime() + "\""; |
1551 | 14ad7326 | pastith | return "\"" + oldBody.getFileSize() + "-" + oldBody.getAuditInfo().getModificationDate().getTime() + "\""; |
1552 | 14ad7326 | pastith | } |
1553 | 14ad7326 | pastith | |
1554 | 14ad7326 | pastith | /**
|
1555 | 14ad7326 | pastith | * URL rewriter.
|
1556 | 14ad7326 | pastith | *
|
1557 | 14ad7326 | pastith | * @param path Path which has to be rewritten
|
1558 | 14ad7326 | pastith | * @return the rewritten URL
|
1559 | 14ad7326 | pastith | */
|
1560 | 31e19588 | Natasa Kapravelou | protected String rewriteUrl(String path) { |
1561 | 14ad7326 | pastith | return urlEncoder.encode(path);
|
1562 | 14ad7326 | pastith | } |
1563 | 14ad7326 | pastith | |
1564 | 14ad7326 | pastith | /**
|
1565 | 14ad7326 | pastith | * Print the lock discovery information associated with a path.
|
1566 | 14ad7326 | pastith | *
|
1567 | 14ad7326 | pastith | * @param path Path
|
1568 | 14ad7326 | pastith | * @param generatedXML XML data to which the locks info will be appended
|
1569 | 14ad7326 | pastith | * @return true if at least one lock was displayed
|
1570 | 14ad7326 | pastith | */
|
1571 | 92012b4d | droutsis | @SuppressWarnings("unused") |
1572 | 92012b4d | droutsis | private boolean generateLockDiscovery(String path, XMLWriter generatedXML) { |
1573 | 14ad7326 | pastith | return false; |
1574 | 14ad7326 | pastith | } |
1575 | 14ad7326 | pastith | |
1576 | 14ad7326 | pastith | /**
|
1577 | 14ad7326 | pastith | * Get creation date in ISO format.
|
1578 | 14ad7326 | pastith | *
|
1579 | 14ad7326 | pastith | * @param creationDate
|
1580 | 14ad7326 | pastith | * @return the formatted date
|
1581 | 14ad7326 | pastith | */
|
1582 | 14ad7326 | pastith | private String getISOCreationDate(long creationDate) { |
1583 | 14ad7326 | pastith | String dateValue = null; |
1584 | 14ad7326 | pastith | synchronized (creationDateFormat) {
|
1585 | 14ad7326 | pastith | dateValue = creationDateFormat.format(new Date(creationDate)); |
1586 | 14ad7326 | pastith | } |
1587 | 14ad7326 | pastith | StringBuffer creationDateValue = new StringBuffer(dateValue); |
1588 | 14ad7326 | pastith | /*
|
1589 | 14ad7326 | pastith | int offset = Calendar.getInstance().getTimeZone().getRawOffset()
|
1590 | 14ad7326 | pastith | / 3600000; // FIXME ?
|
1591 | 14ad7326 | pastith | if (offset < 0) {
|
1592 | 14ad7326 | pastith | creationDateValue.append("-");
|
1593 | 14ad7326 | pastith | offset = -offset;
|
1594 | 14ad7326 | pastith | } else if (offset > 0) {
|
1595 | 14ad7326 | pastith | creationDateValue.append("+");
|
1596 | 14ad7326 | pastith | }
|
1597 | 14ad7326 | pastith | if (offset != 0) {
|
1598 | 14ad7326 | pastith | if (offset < 10)
|
1599 | 14ad7326 | pastith | creationDateValue.append("0");
|
1600 | 14ad7326 | pastith | creationDateValue.append(offset + ":00");
|
1601 | 14ad7326 | pastith | } else {
|
1602 | 14ad7326 | pastith | creationDateValue.append("Z");
|
1603 | 14ad7326 | pastith | }
|
1604 | 14ad7326 | pastith | */
|
1605 | 14ad7326 | pastith | return creationDateValue.toString();
|
1606 | 14ad7326 | pastith | } |
1607 | 14ad7326 | pastith | |
1608 | 14ad7326 | pastith | /**
|
1609 | 14ad7326 | pastith | * Determines the methods normally allowed for the resource.
|
1610 | 14ad7326 | pastith | *
|
1611 | 14ad7326 | pastith | * @param req the HTTP request
|
1612 | 14ad7326 | pastith | * @return a list of the allowed methods
|
1613 | 14ad7326 | pastith | * @throws RpcException if there is an error while communicating with the
|
1614 | 14ad7326 | pastith | * backend
|
1615 | 14ad7326 | pastith | */
|
1616 | 14ad7326 | pastith | private StringBuffer determineMethodsAllowed(HttpServletRequest req) throws RpcException { |
1617 | 14ad7326 | pastith | StringBuffer methodsAllowed = new StringBuffer(); |
1618 | 14ad7326 | pastith | boolean exists = true; |
1619 | 14ad7326 | pastith | Object object = null; |
1620 | 14ad7326 | pastith | User user = getUser(req); |
1621 | 14ad7326 | pastith | String path = getRelativePath(req);
|
1622 | 14ad7326 | pastith | if (user == null && "/".equals(path)) |
1623 | 14ad7326 | pastith | // Special case: OPTIONS request before authentication
|
1624 | a3b6a99f | Panagiotis Astithas | return new StringBuffer("OPTIONS, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND, PUT, MKCOL"); |
1625 | 14ad7326 | pastith | try {
|
1626 | 68410d59 | pastith | object = getService().getResourceAtPath(user.getId(), path, true);
|
1627 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
1628 | 14ad7326 | pastith | exists = false;
|
1629 | 14ad7326 | pastith | } |
1630 | 14ad7326 | pastith | |
1631 | 14ad7326 | pastith | if (!exists) {
|
1632 | 14ad7326 | pastith | methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK");
|
1633 | 14ad7326 | pastith | return methodsAllowed;
|
1634 | 14ad7326 | pastith | } |
1635 | 14ad7326 | pastith | |
1636 | 14ad7326 | pastith | methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
|
1637 | 14ad7326 | pastith | methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");
|
1638 | 14ad7326 | pastith | methodsAllowed.append(", PROPFIND");
|
1639 | 14ad7326 | pastith | |
1640 | f7c44c33 | fstamatelopoulos | if (!(object instanceof Folder)) |
1641 | 14ad7326 | pastith | methodsAllowed.append(", PUT");
|
1642 | 14ad7326 | pastith | |
1643 | 14ad7326 | pastith | return methodsAllowed;
|
1644 | 14ad7326 | pastith | } |
1645 | 14ad7326 | pastith | |
1646 | 14ad7326 | pastith | /**
|
1647 | 14ad7326 | pastith | * Check to see if a resource is currently write locked. The method will
|
1648 | 14ad7326 | pastith | * look at the "If" header to make sure the client has given the appropriate
|
1649 | 14ad7326 | pastith | * lock tokens.
|
1650 | 14ad7326 | pastith | *
|
1651 | 14ad7326 | pastith | * @param req the HTTP request
|
1652 | 14ad7326 | pastith | * @return boolean true if the resource is locked (and no appropriate lock
|
1653 | 14ad7326 | pastith | * token has been found for at least one of the non-shared locks
|
1654 | 14ad7326 | pastith | * which are present on the resource).
|
1655 | 14ad7326 | pastith | */
|
1656 | 92012b4d | droutsis | private boolean isLocked(@SuppressWarnings("unused") HttpServletRequest req) { |
1657 | 92012b4d | droutsis | return false; |
1658 | 14ad7326 | pastith | } |
1659 | 14ad7326 | pastith | |
1660 | 14ad7326 | pastith | /**
|
1661 | 14ad7326 | pastith | * Check to see if a resource is currently write locked.
|
1662 | 14ad7326 | pastith | *
|
1663 | 14ad7326 | pastith | * @param path Path of the resource
|
1664 | 14ad7326 | pastith | * @param ifHeader "If" HTTP header which was included in the request
|
1665 | 14ad7326 | pastith | * @return boolean true if the resource is locked (and no appropriate lock
|
1666 | 14ad7326 | pastith | * token has been found for at least one of the non-shared locks
|
1667 | 14ad7326 | pastith | * which are present on the resource).
|
1668 | 14ad7326 | pastith | */
|
1669 | 92012b4d | droutsis | private boolean isLocked(@SuppressWarnings("unused") String path, @SuppressWarnings("unused") String ifHeader) { |
1670 | 14ad7326 | pastith | return false; |
1671 | 14ad7326 | pastith | } |
1672 | 14ad7326 | pastith | |
1673 | 14ad7326 | pastith | /**
|
1674 | 14ad7326 | pastith | * Parse the content-range header.
|
1675 | 14ad7326 | pastith | *
|
1676 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
1677 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
1678 | 14ad7326 | pastith | * @return Range
|
1679 | 14ad7326 | pastith | * @throws IOException
|
1680 | 14ad7326 | pastith | */
|
1681 | 14ad7326 | pastith | protected Range parseContentRange(HttpServletRequest request, HttpServletResponse response) throws IOException { |
1682 | 14ad7326 | pastith | |
1683 | 14ad7326 | pastith | // Retrieving the content-range header (if any is specified
|
1684 | 14ad7326 | pastith | String rangeHeader = request.getHeader("Content-Range"); |
1685 | 14ad7326 | pastith | |
1686 | 14ad7326 | pastith | if (rangeHeader == null) |
1687 | 14ad7326 | pastith | return null; |
1688 | 14ad7326 | pastith | |
1689 | 14ad7326 | pastith | // bytes is the only range unit supported
|
1690 | 14ad7326 | pastith | if (!rangeHeader.startsWith("bytes")) { |
1691 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_BAD_REQUEST); |
1692 | 14ad7326 | pastith | return null; |
1693 | 14ad7326 | pastith | } |
1694 | 14ad7326 | pastith | |
1695 | 14ad7326 | pastith | rangeHeader = rangeHeader.substring(6).trim();
|
1696 | 14ad7326 | pastith | |
1697 | 14ad7326 | pastith | int dashPos = rangeHeader.indexOf('-'); |
1698 | 14ad7326 | pastith | int slashPos = rangeHeader.indexOf('/'); |
1699 | 14ad7326 | pastith | |
1700 | 14ad7326 | pastith | if (dashPos == -1) { |
1701 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_BAD_REQUEST); |
1702 | 14ad7326 | pastith | return null; |
1703 | 14ad7326 | pastith | } |
1704 | 14ad7326 | pastith | |
1705 | 14ad7326 | pastith | if (slashPos == -1) { |
1706 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_BAD_REQUEST); |
1707 | 14ad7326 | pastith | return null; |
1708 | 14ad7326 | pastith | } |
1709 | 14ad7326 | pastith | |
1710 | 14ad7326 | pastith | Range range = new Range();
|
1711 | 14ad7326 | pastith | |
1712 | 14ad7326 | pastith | try {
|
1713 | 14ad7326 | pastith | range.start = Long.parseLong(rangeHeader.substring(0, dashPos)); |
1714 | 14ad7326 | pastith | range.end = Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos)); |
1715 | 14ad7326 | pastith | range.length = Long.parseLong(rangeHeader.substring(slashPos + 1, rangeHeader.length())); |
1716 | 14ad7326 | pastith | } catch (NumberFormatException e) { |
1717 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_BAD_REQUEST); |
1718 | 14ad7326 | pastith | return null; |
1719 | 14ad7326 | pastith | } |
1720 | 14ad7326 | pastith | |
1721 | 14ad7326 | pastith | if (!range.validate()) {
|
1722 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_BAD_REQUEST); |
1723 | 14ad7326 | pastith | return null; |
1724 | 14ad7326 | pastith | } |
1725 | 14ad7326 | pastith | |
1726 | 14ad7326 | pastith | return range;
|
1727 | 14ad7326 | pastith | |
1728 | 14ad7326 | pastith | } |
1729 | 14ad7326 | pastith | |
1730 | 14ad7326 | pastith | /**
|
1731 | 14ad7326 | pastith | * Handle a partial PUT. New content specified in request is appended to
|
1732 | 14ad7326 | pastith | * existing content in oldRevisionContent (if present). This code does not
|
1733 | 14ad7326 | pastith | * support simultaneous partial updates to the same resource.
|
1734 | 14ad7326 | pastith | *
|
1735 | 14ad7326 | pastith | * @param req
|
1736 | 14ad7326 | pastith | * @param range
|
1737 | 14ad7326 | pastith | * @param path
|
1738 | 14ad7326 | pastith | * @return
|
1739 | 14ad7326 | pastith | * @throws IOException
|
1740 | 14ad7326 | pastith | * @throws RpcException
|
1741 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
1742 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
1743 | 14ad7326 | pastith | */
|
1744 | 14ad7326 | pastith | protected File executePartialPut(HttpServletRequest req, Range range, String path) throws IOException, RpcException, ObjectNotFoundException, InsufficientPermissionsException { |
1745 | 14ad7326 | pastith | // Append data specified in ranges to existing content for this
|
1746 | 14ad7326 | pastith | // resource - create a temporary file on the local file system to
|
1747 | 14ad7326 | pastith | // perform this operation.
|
1748 | 14ad7326 | pastith | File tempDir = (File) getServletContext().getAttribute("javax.servlet.context.tempdir"); |
1749 | 14ad7326 | pastith | // Convert all '/' characters to '.' in resourcePath
|
1750 | 14ad7326 | pastith | String convertedResourcePath = path.replace('/', '.'); |
1751 | 14ad7326 | pastith | File contentFile = new File(tempDir, convertedResourcePath); |
1752 | 14ad7326 | pastith | if (contentFile.createNewFile())
|
1753 | 14ad7326 | pastith | // Clean up contentFile when Tomcat is terminated.
|
1754 | 14ad7326 | pastith | contentFile.deleteOnExit(); |
1755 | 14ad7326 | pastith | |
1756 | 14ad7326 | pastith | RandomAccessFile randAccessContentFile = new RandomAccessFile(contentFile, "rw"); |
1757 | 14ad7326 | pastith | |
1758 | 14ad7326 | pastith | User user = getUser(req); |
1759 | 14ad7326 | pastith | User owner = getOwner(req); |
1760 | f7c44c33 | fstamatelopoulos | FileHeader oldResource = null;
|
1761 | 14ad7326 | pastith | try {
|
1762 | 68410d59 | pastith | Object obj = getService().getResourceAtPath(owner.getId(), path, true); |
1763 | f7c44c33 | fstamatelopoulos | if (obj instanceof FileHeader) |
1764 | f7c44c33 | fstamatelopoulos | oldResource = (FileHeader) obj; |
1765 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
1766 | 14ad7326 | pastith | // Do nothing.
|
1767 | 14ad7326 | pastith | } |
1768 | 14ad7326 | pastith | |
1769 | 14ad7326 | pastith | // Copy data in oldRevisionContent to contentFile
|
1770 | 14ad7326 | pastith | if (oldResource != null) { |
1771 | 14ad7326 | pastith | InputStream contents = getService().getFileContents(user.getId(), oldResource.getId());
|
1772 | 14ad7326 | pastith | BufferedInputStream bufOldRevStream = new BufferedInputStream(contents, BUFFER_SIZE); |
1773 | 14ad7326 | pastith | |
1774 | 14ad7326 | pastith | int numBytesRead;
|
1775 | 14ad7326 | pastith | byte[] copyBuffer = new byte[BUFFER_SIZE]; |
1776 | 14ad7326 | pastith | while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) |
1777 | 14ad7326 | pastith | randAccessContentFile.write(copyBuffer, 0, numBytesRead);
|
1778 | 14ad7326 | pastith | |
1779 | 14ad7326 | pastith | bufOldRevStream.close(); |
1780 | 14ad7326 | pastith | } |
1781 | 14ad7326 | pastith | |
1782 | 14ad7326 | pastith | randAccessContentFile.setLength(range.length); |
1783 | 14ad7326 | pastith | |
1784 | 14ad7326 | pastith | // Append data in request input stream to contentFile
|
1785 | 14ad7326 | pastith | randAccessContentFile.seek(range.start); |
1786 | 14ad7326 | pastith | int numBytesRead;
|
1787 | 14ad7326 | pastith | byte[] transferBuffer = new byte[BUFFER_SIZE]; |
1788 | 14ad7326 | pastith | BufferedInputStream requestBufInStream = new BufferedInputStream(req.getInputStream(), BUFFER_SIZE); |
1789 | 14ad7326 | pastith | while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) |
1790 | 14ad7326 | pastith | randAccessContentFile.write(transferBuffer, 0, numBytesRead);
|
1791 | 14ad7326 | pastith | randAccessContentFile.close(); |
1792 | 14ad7326 | pastith | requestBufInStream.close(); |
1793 | 14ad7326 | pastith | |
1794 | 14ad7326 | pastith | return contentFile;
|
1795 | 14ad7326 | pastith | |
1796 | 14ad7326 | pastith | } |
1797 | 14ad7326 | pastith | |
1798 | 14ad7326 | pastith | /**
|
1799 | 14ad7326 | pastith | * Serve the specified resource, optionally including the data content.
|
1800 | 14ad7326 | pastith | *
|
1801 | 14ad7326 | pastith | * @param req The servlet request we are processing
|
1802 | 14ad7326 | pastith | * @param resp The servlet response we are creating
|
1803 | 14ad7326 | pastith | * @param content Should the content be included?
|
1804 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
1805 | 14ad7326 | pastith | * @exception ServletException if a servlet-specified error occurs
|
1806 | 14ad7326 | pastith | * @throws RpcException
|
1807 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
1808 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
1809 | 14ad7326 | pastith | */
|
1810 | 14ad7326 | pastith | protected void serveResource(HttpServletRequest req, HttpServletResponse resp, boolean content) throws IOException, ServletException, ObjectNotFoundException, InsufficientPermissionsException, RpcException { |
1811 | 14ad7326 | pastith | |
1812 | 14ad7326 | pastith | // Identify the requested resource path
|
1813 | 14ad7326 | pastith | String path = getRelativePath(req);
|
1814 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
1815 | 14ad7326 | pastith | if (content)
|
1816 | 14ad7326 | pastith | logger.debug("Serving resource '" + path + "' headers and data"); |
1817 | 14ad7326 | pastith | else
|
1818 | 14ad7326 | pastith | logger.debug("Serving resource '" + path + "' headers only"); |
1819 | 14ad7326 | pastith | |
1820 | 14ad7326 | pastith | User user = getUser(req); |
1821 | 14ad7326 | pastith | boolean exists = true; |
1822 | 14ad7326 | pastith | Object resource = null; |
1823 | f7c44c33 | fstamatelopoulos | FileHeader file = null;
|
1824 | f7c44c33 | fstamatelopoulos | Folder folder = null;
|
1825 | 14ad7326 | pastith | try {
|
1826 | 68410d59 | pastith | resource = getService().getResourceAtPath(user.getId(), path, true);
|
1827 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
1828 | 14ad7326 | pastith | exists = false;
|
1829 | 14ad7326 | pastith | } catch (RpcException e) {
|
1830 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path); |
1831 | 14ad7326 | pastith | return;
|
1832 | 14ad7326 | pastith | } |
1833 | 14ad7326 | pastith | |
1834 | 14ad7326 | pastith | if (!exists) {
|
1835 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI()); |
1836 | 14ad7326 | pastith | return;
|
1837 | 14ad7326 | pastith | } |
1838 | 14ad7326 | pastith | |
1839 | f7c44c33 | fstamatelopoulos | if (resource instanceof Folder) |
1840 | f7c44c33 | fstamatelopoulos | folder = (Folder) resource; |
1841 | 14ad7326 | pastith | else
|
1842 | f7c44c33 | fstamatelopoulos | file = (FileHeader) resource; |
1843 | 14ad7326 | pastith | |
1844 | 14ad7326 | pastith | // If the resource is not a collection, and the resource path
|
1845 | 14ad7326 | pastith | // ends with "/" or "\", return NOT FOUND
|
1846 | 14ad7326 | pastith | if (folder == null) |
1847 | 14ad7326 | pastith | if (path.endsWith("/") || path.endsWith("\\")) { |
1848 | 14ad7326 | pastith | resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI()); |
1849 | 14ad7326 | pastith | return;
|
1850 | 14ad7326 | pastith | } |
1851 | 14ad7326 | pastith | |
1852 | 14ad7326 | pastith | // Check if the conditions specified in the optional If headers are
|
1853 | 14ad7326 | pastith | // satisfied.
|
1854 | 14ad7326 | pastith | if (folder == null) |
1855 | 14ad7326 | pastith | // Checking If headers
|
1856 | 14ad7326 | pastith | if (!checkIfHeaders(req, resp, file, null)) |
1857 | 14ad7326 | pastith | return;
|
1858 | 14ad7326 | pastith | |
1859 | 14ad7326 | pastith | // Find content type.
|
1860 | 14ad7326 | pastith | String contentType = null; |
1861 | 14ad7326 | pastith | if (file != null) { |
1862 | f7c44c33 | fstamatelopoulos | contentType = file.getCurrentBody().getMimeType(); |
1863 | 14ad7326 | pastith | if (contentType == null) { |
1864 | 14ad7326 | pastith | contentType = getServletContext().getMimeType(file.getName()); |
1865 | f7c44c33 | fstamatelopoulos | file.getCurrentBody().setMimeType(contentType); |
1866 | 14ad7326 | pastith | } |
1867 | 14ad7326 | pastith | } else
|
1868 | 14ad7326 | pastith | contentType = "text/html;charset=UTF-8";
|
1869 | 14ad7326 | pastith | |
1870 | 14ad7326 | pastith | ArrayList ranges = null; |
1871 | 14ad7326 | pastith | long contentLength = -1L; |
1872 | 14ad7326 | pastith | |
1873 | 14ad7326 | pastith | if (file != null) { |
1874 | 46849d14 | pastith | // Accept ranges header
|
1875 | 46849d14 | pastith | resp.setHeader("Accept-Ranges", "bytes"); |
1876 | 14ad7326 | pastith | // Parse range specifier
|
1877 | 14ad7326 | pastith | ranges = parseRange(req, resp, file, null);
|
1878 | 14ad7326 | pastith | // ETag header
|
1879 | 14ad7326 | pastith | resp.setHeader("ETag", getETag(file, null)); |
1880 | 14ad7326 | pastith | // Last-Modified header
|
1881 | 14ad7326 | pastith | resp.setHeader("Last-Modified", getLastModifiedHttp(file.getAuditInfo()));
|
1882 | 14ad7326 | pastith | // Get content length
|
1883 | f7c44c33 | fstamatelopoulos | contentLength = file.getCurrentBody().getFileSize(); |
1884 | 14ad7326 | pastith | // Special case for zero length files, which would cause a
|
1885 | 14ad7326 | pastith | // (silent) ISE when setting the output buffer size
|
1886 | 14ad7326 | pastith | if (contentLength == 0L) |
1887 | 14ad7326 | pastith | content = false;
|
1888 | 14ad7326 | pastith | } |
1889 | 14ad7326 | pastith | |
1890 | 14ad7326 | pastith | ServletOutputStream ostream = null;
|
1891 | 14ad7326 | pastith | PrintWriter writer = null; |
1892 | 14ad7326 | pastith | |
1893 | 14ad7326 | pastith | if (content)
|
1894 | 14ad7326 | pastith | try {
|
1895 | 14ad7326 | pastith | ostream = resp.getOutputStream(); |
1896 | 14ad7326 | pastith | } catch (IllegalStateException e) { |
1897 | 14ad7326 | pastith | // If it fails, we try to get a Writer instead if we're
|
1898 | 14ad7326 | pastith | // trying to serve a text file
|
1899 | 14ad7326 | pastith | if (contentType == null || contentType.startsWith("text") || contentType.endsWith("xml")) |
1900 | 14ad7326 | pastith | writer = resp.getWriter(); |
1901 | 14ad7326 | pastith | else
|
1902 | 14ad7326 | pastith | throw e;
|
1903 | 14ad7326 | pastith | } |
1904 | 14ad7326 | pastith | |
1905 | 14ad7326 | pastith | if (folder != null || (ranges == null || ranges.isEmpty()) && req.getHeader("Range") == null || ranges == FULL) { |
1906 | 14ad7326 | pastith | // Set the appropriate output headers
|
1907 | 14ad7326 | pastith | if (contentType != null) { |
1908 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
1909 | 14ad7326 | pastith | logger.debug("DefaultServlet.serveFile: contentType='" + contentType + "'"); |
1910 | 14ad7326 | pastith | resp.setContentType(contentType); |
1911 | 14ad7326 | pastith | } |
1912 | 14ad7326 | pastith | if (file != null && contentLength >= 0) { |
1913 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
1914 | 14ad7326 | pastith | logger.debug("DefaultServlet.serveFile: contentLength=" + contentLength);
|
1915 | 14ad7326 | pastith | if (contentLength < Integer.MAX_VALUE) |
1916 | 14ad7326 | pastith | resp.setContentLength((int) contentLength);
|
1917 | 14ad7326 | pastith | else
|
1918 | 14ad7326 | pastith | // Set the content-length as String to be able to use a long
|
1919 | 14ad7326 | pastith | resp.setHeader("content-length", "" + contentLength); |
1920 | 14ad7326 | pastith | } |
1921 | 14ad7326 | pastith | |
1922 | 14ad7326 | pastith | InputStream renderResult = null; |
1923 | 14ad7326 | pastith | if (folder != null) |
1924 | 14ad7326 | pastith | if (content)
|
1925 | 14ad7326 | pastith | // Serve the directory browser
|
1926 | 14ad7326 | pastith | renderResult = renderHtml(req.getContextPath(), path, folder, req); |
1927 | 14ad7326 | pastith | |
1928 | 14ad7326 | pastith | // Copy the input stream to our output stream (if requested)
|
1929 | 14ad7326 | pastith | if (content) {
|
1930 | 14ad7326 | pastith | try {
|
1931 | 14ad7326 | pastith | resp.setBufferSize(output); |
1932 | 14ad7326 | pastith | } catch (IllegalStateException e) { |
1933 | 14ad7326 | pastith | // Silent catch
|
1934 | 14ad7326 | pastith | } |
1935 | 14ad7326 | pastith | if (ostream != null) |
1936 | 14ad7326 | pastith | copy(file, renderResult, ostream, req, null);
|
1937 | 14ad7326 | pastith | else
|
1938 | 14ad7326 | pastith | copy(file, renderResult, writer, req, null);
|
1939 | 2f1a60e0 | Dimitris Routsis | updateAccounting(user, new Date(), contentLength); |
1940 | 14ad7326 | pastith | } |
1941 | 14ad7326 | pastith | } else {
|
1942 | 14ad7326 | pastith | if (ranges == null || ranges.isEmpty()) |
1943 | 14ad7326 | pastith | return;
|
1944 | 14ad7326 | pastith | // Partial content response.
|
1945 | 14ad7326 | pastith | resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); |
1946 | 14ad7326 | pastith | |
1947 | 14ad7326 | pastith | if (ranges.size() == 1) { |
1948 | 14ad7326 | pastith | Range range = (Range) ranges.get(0);
|
1949 | 14ad7326 | pastith | resp.addHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + range.length); |
1950 | 14ad7326 | pastith | long length = range.end - range.start + 1; |
1951 | 14ad7326 | pastith | if (length < Integer.MAX_VALUE) |
1952 | 14ad7326 | pastith | resp.setContentLength((int) length);
|
1953 | 14ad7326 | pastith | else
|
1954 | 14ad7326 | pastith | // Set the content-length as String to be able to use a long
|
1955 | 14ad7326 | pastith | resp.setHeader("content-length", "" + length); |
1956 | 14ad7326 | pastith | |
1957 | 14ad7326 | pastith | if (contentType != null) { |
1958 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
1959 | 14ad7326 | pastith | logger.debug("DefaultServlet.serveFile: contentType='" + contentType + "'"); |
1960 | 14ad7326 | pastith | resp.setContentType(contentType); |
1961 | 14ad7326 | pastith | } |
1962 | 14ad7326 | pastith | |
1963 | 14ad7326 | pastith | if (content) {
|
1964 | 14ad7326 | pastith | try {
|
1965 | 14ad7326 | pastith | resp.setBufferSize(output); |
1966 | 14ad7326 | pastith | } catch (IllegalStateException e) { |
1967 | 14ad7326 | pastith | // Silent catch
|
1968 | 14ad7326 | pastith | } |
1969 | 14ad7326 | pastith | if (ostream != null) |
1970 | 14ad7326 | pastith | copy(file, ostream, range, req, null);
|
1971 | 14ad7326 | pastith | else
|
1972 | 14ad7326 | pastith | copy(file, writer, range, req, null);
|
1973 | 2f1a60e0 | Dimitris Routsis | updateAccounting(user, new Date(), contentLength); |
1974 | 14ad7326 | pastith | } |
1975 | 14ad7326 | pastith | |
1976 | 14ad7326 | pastith | } else {
|
1977 | 14ad7326 | pastith | |
1978 | 14ad7326 | pastith | resp.setContentType("multipart/byteranges; boundary=" + mimeSeparation);
|
1979 | 14ad7326 | pastith | |
1980 | 14ad7326 | pastith | if (content) {
|
1981 | 14ad7326 | pastith | try {
|
1982 | 14ad7326 | pastith | resp.setBufferSize(output); |
1983 | 14ad7326 | pastith | } catch (IllegalStateException e) { |
1984 | 14ad7326 | pastith | // Silent catch
|
1985 | 14ad7326 | pastith | } |
1986 | 14ad7326 | pastith | if (ostream != null) |
1987 | 14ad7326 | pastith | copy(file, ostream, ranges.iterator(), contentType, req, null);
|
1988 | 14ad7326 | pastith | else
|
1989 | 14ad7326 | pastith | copy(file, writer, ranges.iterator(), contentType, req, null);
|
1990 | 14ad7326 | pastith | } |
1991 | 14ad7326 | pastith | |
1992 | 14ad7326 | pastith | } |
1993 | 14ad7326 | pastith | |
1994 | 14ad7326 | pastith | } |
1995 | 14ad7326 | pastith | |
1996 | 14ad7326 | pastith | } |
1997 | 14ad7326 | pastith | |
1998 | 14ad7326 | pastith | /**
|
1999 | 14ad7326 | pastith | * Retrieve the last modified date of a resource in HTTP format.
|
2000 | 14ad7326 | pastith | *
|
2001 | 14ad7326 | pastith | * @param auditInfo the audit info for the specified resource
|
2002 | 14ad7326 | pastith | * @return the last modified date in HTTP format
|
2003 | 14ad7326 | pastith | */
|
2004 | f7c44c33 | fstamatelopoulos | protected String getLastModifiedHttp(AuditInfo auditInfo) { |
2005 | 14ad7326 | pastith | Date modifiedDate = auditInfo.getModificationDate();
|
2006 | 14ad7326 | pastith | if (modifiedDate == null) |
2007 | 14ad7326 | pastith | modifiedDate = auditInfo.getCreationDate(); |
2008 | 14ad7326 | pastith | if (modifiedDate == null) |
2009 | 14ad7326 | pastith | modifiedDate = new Date(); |
2010 | 14ad7326 | pastith | String lastModifiedHttp = null; |
2011 | 14ad7326 | pastith | synchronized (format) {
|
2012 | 14ad7326 | pastith | lastModifiedHttp = format.format(modifiedDate); |
2013 | 14ad7326 | pastith | } |
2014 | 14ad7326 | pastith | return lastModifiedHttp;
|
2015 | 14ad7326 | pastith | } |
2016 | 14ad7326 | pastith | |
2017 | 14ad7326 | pastith | /**
|
2018 | 14ad7326 | pastith | * Parse the range header.
|
2019 | 14ad7326 | pastith | *
|
2020 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
2021 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
2022 | 14ad7326 | pastith | * @param file
|
2023 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2024 | 14ad7326 | pastith | * @return Vector of ranges
|
2025 | 14ad7326 | pastith | * @throws IOException
|
2026 | 14ad7326 | pastith | */
|
2027 | f7c44c33 | fstamatelopoulos | protected ArrayList parseRange(HttpServletRequest request, HttpServletResponse response, FileHeader file, FileBody oldBody) throws IOException { |
2028 | 14ad7326 | pastith | // Checking If-Range
|
2029 | 14ad7326 | pastith | String headerValue = request.getHeader("If-Range"); |
2030 | 14ad7326 | pastith | if (headerValue != null) { |
2031 | 14ad7326 | pastith | long headerValueTime = -1L; |
2032 | 14ad7326 | pastith | try {
|
2033 | 14ad7326 | pastith | headerValueTime = request.getDateHeader("If-Range");
|
2034 | 14ad7326 | pastith | } catch (IllegalArgumentException e) { |
2035 | 14ad7326 | pastith | // Do nothing.
|
2036 | 14ad7326 | pastith | } |
2037 | 14ad7326 | pastith | |
2038 | 14ad7326 | pastith | String eTag = getETag(file, oldBody);
|
2039 | 14ad7326 | pastith | long lastModified = oldBody == null ? |
2040 | 14ad7326 | pastith | file.getAuditInfo().getModificationDate().getTime() : |
2041 | 14ad7326 | pastith | oldBody.getAuditInfo().getModificationDate().getTime(); |
2042 | 14ad7326 | pastith | |
2043 | 14ad7326 | pastith | if (headerValueTime == -1L) { |
2044 | 14ad7326 | pastith | // If the ETag the client gave does not match the entity
|
2045 | 14ad7326 | pastith | // etag, then the entire entity is returned.
|
2046 | 14ad7326 | pastith | if (!eTag.equals(headerValue.trim()))
|
2047 | 14ad7326 | pastith | return FULL;
|
2048 | 14ad7326 | pastith | } else
|
2049 | 14ad7326 | pastith | // If the timestamp of the entity the client got is older than
|
2050 | 14ad7326 | pastith | // the last modification date of the entity, the entire entity
|
2051 | 14ad7326 | pastith | // is returned.
|
2052 | 14ad7326 | pastith | if (lastModified > headerValueTime + 1000) |
2053 | 14ad7326 | pastith | return FULL;
|
2054 | 14ad7326 | pastith | } |
2055 | 14ad7326 | pastith | |
2056 | f7c44c33 | fstamatelopoulos | long fileLength = oldBody == null ? file.getCurrentBody().getFileSize() : oldBody.getFileSize(); |
2057 | 14ad7326 | pastith | if (fileLength == 0) |
2058 | 14ad7326 | pastith | return null; |
2059 | 14ad7326 | pastith | |
2060 | 14ad7326 | pastith | // Retrieving the range header (if any is specified).
|
2061 | 14ad7326 | pastith | String rangeHeader = request.getHeader("Range"); |
2062 | 14ad7326 | pastith | |
2063 | 14ad7326 | pastith | if (rangeHeader == null) |
2064 | 14ad7326 | pastith | return null; |
2065 | 14ad7326 | pastith | // bytes is the only range unit supported (and I don't see the point
|
2066 | 14ad7326 | pastith | // of adding new ones).
|
2067 | 14ad7326 | pastith | if (!rangeHeader.startsWith("bytes")) { |
2068 | 14ad7326 | pastith | response.addHeader("Content-Range", "bytes */" + fileLength); |
2069 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
2070 | 14ad7326 | pastith | return null; |
2071 | 14ad7326 | pastith | } |
2072 | 14ad7326 | pastith | |
2073 | 14ad7326 | pastith | rangeHeader = rangeHeader.substring(6);
|
2074 | 14ad7326 | pastith | |
2075 | 14ad7326 | pastith | // Vector that will contain all the ranges which are successfully
|
2076 | 14ad7326 | pastith | // parsed.
|
2077 | 14ad7326 | pastith | ArrayList<Range> result = new ArrayList<Range>(); |
2078 | 14ad7326 | pastith | StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ","); |
2079 | 14ad7326 | pastith | // Parsing the range list
|
2080 | 14ad7326 | pastith | while (commaTokenizer.hasMoreTokens()) {
|
2081 | 14ad7326 | pastith | String rangeDefinition = commaTokenizer.nextToken().trim();
|
2082 | 14ad7326 | pastith | |
2083 | 14ad7326 | pastith | Range currentRange = new Range();
|
2084 | 14ad7326 | pastith | currentRange.length = fileLength; |
2085 | 14ad7326 | pastith | |
2086 | 14ad7326 | pastith | int dashPos = rangeDefinition.indexOf('-'); |
2087 | 14ad7326 | pastith | |
2088 | 14ad7326 | pastith | if (dashPos == -1) { |
2089 | 14ad7326 | pastith | response.addHeader("Content-Range", "bytes */" + fileLength); |
2090 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
2091 | 14ad7326 | pastith | return null; |
2092 | 14ad7326 | pastith | } |
2093 | 14ad7326 | pastith | |
2094 | 14ad7326 | pastith | if (dashPos == 0) |
2095 | 14ad7326 | pastith | try {
|
2096 | 14ad7326 | pastith | long offset = Long.parseLong(rangeDefinition); |
2097 | 14ad7326 | pastith | currentRange.start = fileLength + offset; |
2098 | 14ad7326 | pastith | currentRange.end = fileLength - 1;
|
2099 | 14ad7326 | pastith | } catch (NumberFormatException e) { |
2100 | 14ad7326 | pastith | response.addHeader("Content-Range", "bytes */" + fileLength); |
2101 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
2102 | 14ad7326 | pastith | return null; |
2103 | 14ad7326 | pastith | } |
2104 | 14ad7326 | pastith | else
|
2105 | 14ad7326 | pastith | try {
|
2106 | 14ad7326 | pastith | currentRange.start = Long.parseLong(rangeDefinition.substring(0, dashPos)); |
2107 | 14ad7326 | pastith | if (dashPos < rangeDefinition.length() - 1) |
2108 | 14ad7326 | pastith | currentRange.end = Long.parseLong(rangeDefinition.substring(dashPos + 1, rangeDefinition.length())); |
2109 | 14ad7326 | pastith | else
|
2110 | 14ad7326 | pastith | currentRange.end = fileLength - 1;
|
2111 | 14ad7326 | pastith | } catch (NumberFormatException e) { |
2112 | 14ad7326 | pastith | response.addHeader("Content-Range", "bytes */" + fileLength); |
2113 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
2114 | 14ad7326 | pastith | return null; |
2115 | 14ad7326 | pastith | } |
2116 | 14ad7326 | pastith | |
2117 | 14ad7326 | pastith | if (!currentRange.validate()) {
|
2118 | 14ad7326 | pastith | response.addHeader("Content-Range", "bytes */" + fileLength); |
2119 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
2120 | 14ad7326 | pastith | return null; |
2121 | 14ad7326 | pastith | } |
2122 | 14ad7326 | pastith | result.add(currentRange); |
2123 | 14ad7326 | pastith | } |
2124 | 14ad7326 | pastith | return result;
|
2125 | 14ad7326 | pastith | } |
2126 | 14ad7326 | pastith | |
2127 | 14ad7326 | pastith | /**
|
2128 | 14ad7326 | pastith | * Check if the conditions specified in the optional If headers are
|
2129 | 14ad7326 | pastith | * satisfied.
|
2130 | 14ad7326 | pastith | *
|
2131 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
2132 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
2133 | 14ad7326 | pastith | * @param file the file resource against which the checks will be made
|
2134 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2135 | 14ad7326 | pastith | * @return boolean true if the resource meets all the specified conditions,
|
2136 | 14ad7326 | pastith | * and false if any of the conditions is not satisfied, in which
|
2137 | 14ad7326 | pastith | * case request processing is stopped
|
2138 | 14ad7326 | pastith | * @throws IOException
|
2139 | 14ad7326 | pastith | */
|
2140 | 14ad7326 | pastith | protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse response, |
2141 | f7c44c33 | fstamatelopoulos | FileHeader file, FileBody oldBody) throws IOException { |
2142 | 14ad7326 | pastith | // TODO : Checking the WebDAV If header
|
2143 | 14ad7326 | pastith | return checkIfMatch(request, response, file, oldBody) &&
|
2144 | 14ad7326 | pastith | checkIfModifiedSince(request, response, file, oldBody) && |
2145 | 14ad7326 | pastith | checkIfNoneMatch(request, response, file, oldBody) && |
2146 | 14ad7326 | pastith | checkIfUnmodifiedSince(request, response, file, oldBody); |
2147 | 14ad7326 | pastith | } |
2148 | 14ad7326 | pastith | |
2149 | 14ad7326 | pastith | /**
|
2150 | 14ad7326 | pastith | * Check if the if-match condition is satisfied.
|
2151 | 14ad7326 | pastith | *
|
2152 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
2153 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
2154 | 14ad7326 | pastith | * @param file the file object
|
2155 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2156 | 14ad7326 | pastith | * @return boolean true if the resource meets the specified condition, and
|
2157 | 14ad7326 | pastith | * false if the condition is not satisfied, in which case request
|
2158 | 14ad7326 | pastith | * processing is stopped
|
2159 | 14ad7326 | pastith | * @throws IOException
|
2160 | 14ad7326 | pastith | */
|
2161 | 14ad7326 | pastith | private boolean checkIfMatch(HttpServletRequest request, HttpServletResponse response, |
2162 | f7c44c33 | fstamatelopoulos | FileHeader file, FileBody oldBody) throws IOException { |
2163 | 14ad7326 | pastith | String eTag = getETag(file, oldBody);
|
2164 | 14ad7326 | pastith | String headerValue = request.getHeader("If-Match"); |
2165 | 14ad7326 | pastith | if (headerValue != null) |
2166 | 14ad7326 | pastith | if (headerValue.indexOf('*') == -1) { |
2167 | 14ad7326 | pastith | StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ","); |
2168 | 14ad7326 | pastith | boolean conditionSatisfied = false; |
2169 | 14ad7326 | pastith | while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
|
2170 | 14ad7326 | pastith | String currentToken = commaTokenizer.nextToken();
|
2171 | 14ad7326 | pastith | if (currentToken.trim().equals(eTag))
|
2172 | 14ad7326 | pastith | conditionSatisfied = true;
|
2173 | 14ad7326 | pastith | } |
2174 | 14ad7326 | pastith | // If none of the given ETags match, 412 Precodition failed is
|
2175 | 14ad7326 | pastith | // sent back.
|
2176 | 14ad7326 | pastith | if (!conditionSatisfied) {
|
2177 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); |
2178 | 14ad7326 | pastith | return false; |
2179 | 14ad7326 | pastith | } |
2180 | 14ad7326 | pastith | } |
2181 | 14ad7326 | pastith | return true; |
2182 | 14ad7326 | pastith | } |
2183 | 14ad7326 | pastith | |
2184 | 14ad7326 | pastith | /**
|
2185 | 14ad7326 | pastith | * Check if the if-modified-since condition is satisfied.
|
2186 | 14ad7326 | pastith | *
|
2187 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
2188 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
2189 | 14ad7326 | pastith | * @param file the file object
|
2190 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2191 | 14ad7326 | pastith | * @return boolean true if the resource meets the specified condition, and
|
2192 | 14ad7326 | pastith | * false if the condition is not satisfied, in which case request
|
2193 | 14ad7326 | pastith | * processing is stopped
|
2194 | 14ad7326 | pastith | */
|
2195 | 14ad7326 | pastith | private boolean checkIfModifiedSince(HttpServletRequest request, |
2196 | f7c44c33 | fstamatelopoulos | HttpServletResponse response, FileHeader file, FileBody oldBody) { |
2197 | 14ad7326 | pastith | try {
|
2198 | 14ad7326 | pastith | long headerValue = request.getDateHeader("If-Modified-Since"); |
2199 | 14ad7326 | pastith | long lastModified = oldBody == null ? |
2200 | 14ad7326 | pastith | file.getAuditInfo().getModificationDate().getTime() : |
2201 | 14ad7326 | pastith | oldBody.getAuditInfo().getModificationDate().getTime(); |
2202 | 14ad7326 | pastith | if (headerValue != -1) |
2203 | 14ad7326 | pastith | // If an If-None-Match header has been specified, if modified
|
2204 | 46849d14 | pastith | // since is ignored.
|
2205 | 14ad7326 | pastith | if (request.getHeader("If-None-Match") == null && lastModified < headerValue + 1000) { |
2206 | 14ad7326 | pastith | // The entity has not been modified since the date
|
2207 | 14ad7326 | pastith | // specified by the client. This is not an error case.
|
2208 | 14ad7326 | pastith | response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); |
2209 | 14ad7326 | pastith | response.setHeader("ETag", getETag(file, oldBody));
|
2210 | 14ad7326 | pastith | return false; |
2211 | 14ad7326 | pastith | } |
2212 | 14ad7326 | pastith | } catch (IllegalArgumentException illegalArgument) { |
2213 | 14ad7326 | pastith | return true; |
2214 | 14ad7326 | pastith | } |
2215 | 14ad7326 | pastith | return true; |
2216 | 14ad7326 | pastith | } |
2217 | 14ad7326 | pastith | |
2218 | 14ad7326 | pastith | /**
|
2219 | 14ad7326 | pastith | * Check if the if-none-match condition is satisfied.
|
2220 | 14ad7326 | pastith | *
|
2221 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
2222 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
2223 | 14ad7326 | pastith | * @param file the file object
|
2224 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2225 | 14ad7326 | pastith | * @return boolean true if the resource meets the specified condition, and
|
2226 | 14ad7326 | pastith | * false if the condition is not satisfied, in which case request
|
2227 | 14ad7326 | pastith | * processing is stopped
|
2228 | 14ad7326 | pastith | * @throws IOException
|
2229 | 14ad7326 | pastith | */
|
2230 | 14ad7326 | pastith | private boolean checkIfNoneMatch(HttpServletRequest request, |
2231 | f7c44c33 | fstamatelopoulos | HttpServletResponse response, FileHeader file, FileBody oldBody) |
2232 | 14ad7326 | pastith | throws IOException { |
2233 | 14ad7326 | pastith | String eTag = getETag(file, oldBody);
|
2234 | 14ad7326 | pastith | String headerValue = request.getHeader("If-None-Match"); |
2235 | 14ad7326 | pastith | if (headerValue != null) { |
2236 | 14ad7326 | pastith | boolean conditionSatisfied = false; |
2237 | 14ad7326 | pastith | if (!headerValue.equals("*")) { |
2238 | 14ad7326 | pastith | StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ","); |
2239 | 14ad7326 | pastith | while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
|
2240 | 14ad7326 | pastith | String currentToken = commaTokenizer.nextToken();
|
2241 | 14ad7326 | pastith | if (currentToken.trim().equals(eTag))
|
2242 | 14ad7326 | pastith | conditionSatisfied = true;
|
2243 | 14ad7326 | pastith | } |
2244 | 14ad7326 | pastith | } else
|
2245 | 14ad7326 | pastith | conditionSatisfied = true;
|
2246 | 14ad7326 | pastith | if (conditionSatisfied) {
|
2247 | 14ad7326 | pastith | // For GET and HEAD, we should respond with 304 Not Modified.
|
2248 | 14ad7326 | pastith | // For every other method, 412 Precondition Failed is sent
|
2249 | 14ad7326 | pastith | // back.
|
2250 | 14ad7326 | pastith | if ("GET".equals(request.getMethod()) || "HEAD".equals(request.getMethod())) { |
2251 | 14ad7326 | pastith | response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); |
2252 | 14ad7326 | pastith | response.setHeader("ETag", getETag(file, oldBody));
|
2253 | 14ad7326 | pastith | return false; |
2254 | 14ad7326 | pastith | } |
2255 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); |
2256 | 14ad7326 | pastith | return false; |
2257 | 14ad7326 | pastith | } |
2258 | 14ad7326 | pastith | } |
2259 | 14ad7326 | pastith | return true; |
2260 | 14ad7326 | pastith | } |
2261 | 14ad7326 | pastith | |
2262 | 14ad7326 | pastith | /**
|
2263 | 14ad7326 | pastith | * Check if the if-unmodified-since condition is satisfied.
|
2264 | 14ad7326 | pastith | *
|
2265 | 14ad7326 | pastith | * @param request The servlet request we are processing
|
2266 | 14ad7326 | pastith | * @param response The servlet response we are creating
|
2267 | 14ad7326 | pastith | * @param file the file object
|
2268 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2269 | 14ad7326 | pastith | * @return boolean true if the resource meets the specified condition, and
|
2270 | 14ad7326 | pastith | * false if the condition is not satisfied, in which case request
|
2271 | 14ad7326 | pastith | * processing is stopped
|
2272 | 14ad7326 | pastith | * @throws IOException
|
2273 | 14ad7326 | pastith | */
|
2274 | 14ad7326 | pastith | private boolean checkIfUnmodifiedSince(HttpServletRequest request, |
2275 | f7c44c33 | fstamatelopoulos | HttpServletResponse response, FileHeader file, FileBody oldBody) |
2276 | 14ad7326 | pastith | throws IOException { |
2277 | 14ad7326 | pastith | try {
|
2278 | 14ad7326 | pastith | long lastModified = oldBody == null ? |
2279 | 14ad7326 | pastith | file.getAuditInfo().getModificationDate().getTime() : |
2280 | 14ad7326 | pastith | oldBody.getAuditInfo().getModificationDate().getTime(); |
2281 | 14ad7326 | pastith | long headerValue = request.getDateHeader("If-Unmodified-Since"); |
2282 | 14ad7326 | pastith | if (headerValue != -1) |
2283 | 14ad7326 | pastith | if (lastModified >= headerValue + 1000) { |
2284 | 14ad7326 | pastith | // The entity has not been modified since the date
|
2285 | 14ad7326 | pastith | // specified by the client. This is not an error case.
|
2286 | 14ad7326 | pastith | response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); |
2287 | 14ad7326 | pastith | return false; |
2288 | 14ad7326 | pastith | } |
2289 | 14ad7326 | pastith | } catch (IllegalArgumentException illegalArgument) { |
2290 | 14ad7326 | pastith | return true; |
2291 | 14ad7326 | pastith | } |
2292 | 14ad7326 | pastith | return true; |
2293 | 14ad7326 | pastith | } |
2294 | 14ad7326 | pastith | |
2295 | 14ad7326 | pastith | /**
|
2296 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2297 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2298 | 14ad7326 | pastith | * the face of an exception).
|
2299 | 14ad7326 | pastith | *
|
2300 | 14ad7326 | pastith | * @param file the file resource
|
2301 | 14ad7326 | pastith | * @param is
|
2302 | 14ad7326 | pastith | * @param ostream The output stream to write to
|
2303 | 14ad7326 | pastith | * @param req the HTTP request
|
2304 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2305 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
2306 | 14ad7326 | pastith | * @throws RpcException
|
2307 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
2308 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
2309 | 14ad7326 | pastith | */
|
2310 | f7c44c33 | fstamatelopoulos | protected void copy(FileHeader file, InputStream is, ServletOutputStream ostream, |
2311 | f7c44c33 | fstamatelopoulos | HttpServletRequest req, FileBody oldBody) throws IOException, |
2312 | 14ad7326 | pastith | ObjectNotFoundException, InsufficientPermissionsException, RpcException { |
2313 | 14ad7326 | pastith | IOException exception = null; |
2314 | 14ad7326 | pastith | InputStream resourceInputStream = null; |
2315 | 14ad7326 | pastith | User user = getUser(req); |
2316 | 14ad7326 | pastith | if (user == null) |
2317 | 14ad7326 | pastith | throw new ObjectNotFoundException("No user or owner specified"); |
2318 | 14ad7326 | pastith | if (file != null) |
2319 | 14ad7326 | pastith | resourceInputStream = oldBody == null ?
|
2320 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId()) : |
2321 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId(), oldBody.getId()); |
2322 | 14ad7326 | pastith | else
|
2323 | 14ad7326 | pastith | resourceInputStream = is; |
2324 | 14ad7326 | pastith | |
2325 | 14ad7326 | pastith | InputStream istream = new BufferedInputStream(resourceInputStream, input); |
2326 | 14ad7326 | pastith | // Copy the input stream to the output stream
|
2327 | 14ad7326 | pastith | exception = copyRange(istream, ostream); |
2328 | 14ad7326 | pastith | // Clean up the input stream
|
2329 | 14ad7326 | pastith | istream.close(); |
2330 | 14ad7326 | pastith | // Rethrow any exception that has occurred
|
2331 | 14ad7326 | pastith | if (exception != null) |
2332 | 14ad7326 | pastith | throw exception;
|
2333 | 14ad7326 | pastith | } |
2334 | 14ad7326 | pastith | |
2335 | 14ad7326 | pastith | /**
|
2336 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2337 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2338 | 14ad7326 | pastith | * the face of an exception).
|
2339 | 14ad7326 | pastith | *
|
2340 | 14ad7326 | pastith | * @param istream The input stream to read from
|
2341 | 14ad7326 | pastith | * @param ostream The output stream to write to
|
2342 | 14ad7326 | pastith | * @return Exception which occurred during processing
|
2343 | 14ad7326 | pastith | */
|
2344 | 14ad7326 | pastith | private IOException copyRange(InputStream istream, ServletOutputStream ostream) { |
2345 | 14ad7326 | pastith | // Copy the input stream to the output stream
|
2346 | 14ad7326 | pastith | IOException exception = null; |
2347 | 14ad7326 | pastith | byte buffer[] = new byte[input]; |
2348 | 14ad7326 | pastith | int len = buffer.length;
|
2349 | 14ad7326 | pastith | while (true) |
2350 | 14ad7326 | pastith | try {
|
2351 | 14ad7326 | pastith | len = istream.read(buffer); |
2352 | 14ad7326 | pastith | if (len == -1) |
2353 | 14ad7326 | pastith | break;
|
2354 | 14ad7326 | pastith | ostream.write(buffer, 0, len);
|
2355 | 14ad7326 | pastith | } catch (IOException e) { |
2356 | 14ad7326 | pastith | exception = e; |
2357 | 14ad7326 | pastith | len = -1;
|
2358 | 14ad7326 | pastith | break;
|
2359 | 14ad7326 | pastith | } |
2360 | 14ad7326 | pastith | return exception;
|
2361 | 14ad7326 | pastith | } |
2362 | 14ad7326 | pastith | |
2363 | 14ad7326 | pastith | /**
|
2364 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2365 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2366 | 14ad7326 | pastith | * the face of an exception).
|
2367 | 14ad7326 | pastith | *
|
2368 | 14ad7326 | pastith | * @param file
|
2369 | 14ad7326 | pastith | * @param is
|
2370 | 14ad7326 | pastith | * @param resourceInfo The resource info
|
2371 | 14ad7326 | pastith | * @param writer The writer to write to
|
2372 | 14ad7326 | pastith | * @param req the HTTP request
|
2373 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2374 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
2375 | 14ad7326 | pastith | * @throws RpcException
|
2376 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
2377 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
2378 | 14ad7326 | pastith | */
|
2379 | f7c44c33 | fstamatelopoulos | protected void copy(FileHeader file, InputStream is, PrintWriter writer, |
2380 | f7c44c33 | fstamatelopoulos | HttpServletRequest req, FileBody oldBody) throws IOException, |
2381 | 14ad7326 | pastith | ObjectNotFoundException, InsufficientPermissionsException, RpcException { |
2382 | 14ad7326 | pastith | IOException exception = null; |
2383 | bdf739a9 | Natasa Kapravelou | |
2384 | 14ad7326 | pastith | User user = getUser(req); |
2385 | 14ad7326 | pastith | InputStream resourceInputStream = null; |
2386 | 14ad7326 | pastith | if (file != null) |
2387 | 14ad7326 | pastith | resourceInputStream = oldBody == null ?
|
2388 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId()) : |
2389 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId(), oldBody.getId()); |
2390 | 14ad7326 | pastith | else
|
2391 | 14ad7326 | pastith | resourceInputStream = is; |
2392 | 14ad7326 | pastith | |
2393 | 14ad7326 | pastith | Reader reader;
|
2394 | 14ad7326 | pastith | if (fileEncoding == null) |
2395 | 14ad7326 | pastith | reader = new InputStreamReader(resourceInputStream); |
2396 | 14ad7326 | pastith | else
|
2397 | 14ad7326 | pastith | reader = new InputStreamReader(resourceInputStream, fileEncoding); |
2398 | 14ad7326 | pastith | |
2399 | 14ad7326 | pastith | // Copy the input stream to the output stream
|
2400 | 14ad7326 | pastith | exception = copyRange(reader, writer); |
2401 | 14ad7326 | pastith | // Clean up the reader
|
2402 | 14ad7326 | pastith | reader.close(); |
2403 | 14ad7326 | pastith | // Rethrow any exception that has occurred
|
2404 | 14ad7326 | pastith | if (exception != null) |
2405 | 14ad7326 | pastith | throw exception;
|
2406 | 14ad7326 | pastith | } |
2407 | 14ad7326 | pastith | |
2408 | 14ad7326 | pastith | /**
|
2409 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2410 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2411 | 14ad7326 | pastith | * the face of an exception).
|
2412 | 14ad7326 | pastith | *
|
2413 | 14ad7326 | pastith | * @param reader The reader to read from
|
2414 | 14ad7326 | pastith | * @param writer The writer to write to
|
2415 | 14ad7326 | pastith | * @return Exception which occurred during processing
|
2416 | 14ad7326 | pastith | */
|
2417 | 14ad7326 | pastith | private IOException copyRange(Reader reader, PrintWriter writer) { |
2418 | 14ad7326 | pastith | // Copy the input stream to the output stream
|
2419 | 14ad7326 | pastith | IOException exception = null; |
2420 | 14ad7326 | pastith | char buffer[] = new char[input]; |
2421 | 14ad7326 | pastith | int len = buffer.length;
|
2422 | 14ad7326 | pastith | while (true) |
2423 | 14ad7326 | pastith | try {
|
2424 | 14ad7326 | pastith | len = reader.read(buffer); |
2425 | 14ad7326 | pastith | if (len == -1) |
2426 | 14ad7326 | pastith | break;
|
2427 | 14ad7326 | pastith | writer.write(buffer, 0, len);
|
2428 | 14ad7326 | pastith | } catch (IOException e) { |
2429 | 14ad7326 | pastith | exception = e; |
2430 | 14ad7326 | pastith | len = -1;
|
2431 | 14ad7326 | pastith | break;
|
2432 | 14ad7326 | pastith | } |
2433 | 14ad7326 | pastith | return exception;
|
2434 | 14ad7326 | pastith | } |
2435 | 14ad7326 | pastith | |
2436 | 14ad7326 | pastith | /**
|
2437 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2438 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2439 | 14ad7326 | pastith | * the face of an exception).
|
2440 | 14ad7326 | pastith | *
|
2441 | 14ad7326 | pastith | * @param file
|
2442 | 14ad7326 | pastith | * @param writer The writer to write to
|
2443 | 14ad7326 | pastith | * @param ranges Enumeration of the ranges the client wanted to retrieve
|
2444 | 14ad7326 | pastith | * @param contentType Content type of the resource
|
2445 | 14ad7326 | pastith | * @param req the HTTP request
|
2446 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2447 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
2448 | 14ad7326 | pastith | * @throws RpcException
|
2449 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
2450 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
2451 | 14ad7326 | pastith | */
|
2452 | f7c44c33 | fstamatelopoulos | protected void copy(FileHeader file, PrintWriter writer, Iterator ranges, |
2453 | f7c44c33 | fstamatelopoulos | String contentType, HttpServletRequest req, FileBody oldBody)
|
2454 | 14ad7326 | pastith | throws IOException, ObjectNotFoundException, InsufficientPermissionsException, RpcException { |
2455 | 14ad7326 | pastith | User user = getUser(req); |
2456 | 14ad7326 | pastith | IOException exception = null; |
2457 | 14ad7326 | pastith | while (exception == null && ranges.hasNext()) { |
2458 | 14ad7326 | pastith | InputStream resourceInputStream = oldBody == null ? |
2459 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId()) : |
2460 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId(), oldBody.getId()); |
2461 | 14ad7326 | pastith | Reader reader;
|
2462 | 14ad7326 | pastith | if (fileEncoding == null) |
2463 | 14ad7326 | pastith | reader = new InputStreamReader(resourceInputStream); |
2464 | 14ad7326 | pastith | else
|
2465 | 14ad7326 | pastith | reader = new InputStreamReader(resourceInputStream, fileEncoding); |
2466 | 14ad7326 | pastith | Range currentRange = (Range) ranges.next(); |
2467 | 14ad7326 | pastith | // Writing MIME header.
|
2468 | 14ad7326 | pastith | writer.println(); |
2469 | 14ad7326 | pastith | writer.println("--" + mimeSeparation);
|
2470 | 14ad7326 | pastith | if (contentType != null) |
2471 | 14ad7326 | pastith | writer.println("Content-Type: " + contentType);
|
2472 | 14ad7326 | pastith | writer.println("Content-Range: bytes " + currentRange.start + "-" + currentRange.end + "/" + currentRange.length); |
2473 | 14ad7326 | pastith | writer.println(); |
2474 | 14ad7326 | pastith | // Printing content
|
2475 | 14ad7326 | pastith | exception = copyRange(reader, writer, currentRange.start, currentRange.end); |
2476 | 14ad7326 | pastith | reader.close(); |
2477 | 14ad7326 | pastith | } |
2478 | 14ad7326 | pastith | writer.println(); |
2479 | 14ad7326 | pastith | writer.print("--" + mimeSeparation + "--"); |
2480 | 14ad7326 | pastith | // Rethrow any exception that has occurred
|
2481 | 14ad7326 | pastith | if (exception != null) |
2482 | 14ad7326 | pastith | throw exception;
|
2483 | 14ad7326 | pastith | } |
2484 | 14ad7326 | pastith | |
2485 | 14ad7326 | pastith | /**
|
2486 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2487 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2488 | 14ad7326 | pastith | * the face of an exception).
|
2489 | 14ad7326 | pastith | *
|
2490 | 14ad7326 | pastith | * @param istream The input stream to read from
|
2491 | 14ad7326 | pastith | * @param ostream The output stream to write to
|
2492 | 14ad7326 | pastith | * @param start Start of the range which will be copied
|
2493 | 14ad7326 | pastith | * @param end End of the range which will be copied
|
2494 | 14ad7326 | pastith | * @return Exception which occurred during processing
|
2495 | 14ad7326 | pastith | */
|
2496 | 14ad7326 | pastith | private IOException copyRange(InputStream istream, ServletOutputStream ostream, long start, long end) { |
2497 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
2498 | 14ad7326 | pastith | logger.debug("Serving bytes:" + start + "-" + end); |
2499 | 14ad7326 | pastith | try {
|
2500 | 14ad7326 | pastith | istream.skip(start); |
2501 | 14ad7326 | pastith | } catch (IOException e) { |
2502 | 14ad7326 | pastith | return e;
|
2503 | 14ad7326 | pastith | } |
2504 | 14ad7326 | pastith | IOException exception = null; |
2505 | 14ad7326 | pastith | long bytesToRead = end - start + 1; |
2506 | 14ad7326 | pastith | byte buffer[] = new byte[input]; |
2507 | 14ad7326 | pastith | int len = buffer.length;
|
2508 | 14ad7326 | pastith | while (bytesToRead > 0 && len >= buffer.length) { |
2509 | 14ad7326 | pastith | try {
|
2510 | 14ad7326 | pastith | len = istream.read(buffer); |
2511 | 14ad7326 | pastith | if (bytesToRead >= len) {
|
2512 | 14ad7326 | pastith | ostream.write(buffer, 0, len);
|
2513 | 14ad7326 | pastith | bytesToRead -= len; |
2514 | 14ad7326 | pastith | } else {
|
2515 | 14ad7326 | pastith | ostream.write(buffer, 0, (int) bytesToRead); |
2516 | 14ad7326 | pastith | bytesToRead = 0;
|
2517 | 14ad7326 | pastith | } |
2518 | 14ad7326 | pastith | } catch (IOException e) { |
2519 | 14ad7326 | pastith | exception = e; |
2520 | 14ad7326 | pastith | len = -1;
|
2521 | 14ad7326 | pastith | } |
2522 | 14ad7326 | pastith | if (len < buffer.length)
|
2523 | 14ad7326 | pastith | break;
|
2524 | 14ad7326 | pastith | } |
2525 | 14ad7326 | pastith | return exception;
|
2526 | 14ad7326 | pastith | } |
2527 | 14ad7326 | pastith | |
2528 | 14ad7326 | pastith | /**
|
2529 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2530 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2531 | 14ad7326 | pastith | * the face of an exception).
|
2532 | 14ad7326 | pastith | *
|
2533 | 14ad7326 | pastith | * @param reader The reader to read from
|
2534 | 14ad7326 | pastith | * @param writer The writer to write to
|
2535 | 14ad7326 | pastith | * @param start Start of the range which will be copied
|
2536 | 14ad7326 | pastith | * @param end End of the range which will be copied
|
2537 | 14ad7326 | pastith | * @return Exception which occurred during processing
|
2538 | 14ad7326 | pastith | */
|
2539 | 14ad7326 | pastith | private IOException copyRange(Reader reader, PrintWriter writer, long start, long end) { |
2540 | 14ad7326 | pastith | try {
|
2541 | 14ad7326 | pastith | reader.skip(start); |
2542 | 14ad7326 | pastith | } catch (IOException e) { |
2543 | 14ad7326 | pastith | return e;
|
2544 | 14ad7326 | pastith | } |
2545 | 14ad7326 | pastith | IOException exception = null; |
2546 | 14ad7326 | pastith | long bytesToRead = end - start + 1; |
2547 | 14ad7326 | pastith | char buffer[] = new char[input]; |
2548 | 14ad7326 | pastith | int len = buffer.length;
|
2549 | 14ad7326 | pastith | while (bytesToRead > 0 && len >= buffer.length) { |
2550 | 14ad7326 | pastith | try {
|
2551 | 14ad7326 | pastith | len = reader.read(buffer); |
2552 | 14ad7326 | pastith | if (bytesToRead >= len) {
|
2553 | 14ad7326 | pastith | writer.write(buffer, 0, len);
|
2554 | 14ad7326 | pastith | bytesToRead -= len; |
2555 | 14ad7326 | pastith | } else {
|
2556 | 14ad7326 | pastith | writer.write(buffer, 0, (int) bytesToRead); |
2557 | 14ad7326 | pastith | bytesToRead = 0;
|
2558 | 14ad7326 | pastith | } |
2559 | 14ad7326 | pastith | } catch (IOException e) { |
2560 | 14ad7326 | pastith | exception = e; |
2561 | 14ad7326 | pastith | len = -1;
|
2562 | 14ad7326 | pastith | } |
2563 | 14ad7326 | pastith | if (len < buffer.length)
|
2564 | 14ad7326 | pastith | break;
|
2565 | 14ad7326 | pastith | } |
2566 | 14ad7326 | pastith | return exception;
|
2567 | 14ad7326 | pastith | } |
2568 | 14ad7326 | pastith | |
2569 | 14ad7326 | pastith | /**
|
2570 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2571 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2572 | 14ad7326 | pastith | * the face of an exception).
|
2573 | 14ad7326 | pastith | *
|
2574 | 14ad7326 | pastith | * @param file
|
2575 | 14ad7326 | pastith | * @param ostream The output stream to write to
|
2576 | 14ad7326 | pastith | * @param range Range the client wanted to retrieve
|
2577 | 14ad7326 | pastith | * @param req the HTTP request
|
2578 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2579 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
2580 | 14ad7326 | pastith | * @throws RpcException
|
2581 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
2582 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
2583 | 14ad7326 | pastith | */
|
2584 | f7c44c33 | fstamatelopoulos | protected void copy(FileHeader file, ServletOutputStream ostream, Range range, |
2585 | f7c44c33 | fstamatelopoulos | HttpServletRequest req, FileBody oldBody) throws IOException, |
2586 | 14ad7326 | pastith | ObjectNotFoundException, InsufficientPermissionsException, RpcException { |
2587 | 14ad7326 | pastith | IOException exception = null; |
2588 | 14ad7326 | pastith | User user = getUser(req); |
2589 | 14ad7326 | pastith | InputStream resourceInputStream = oldBody == null ? |
2590 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId()) : |
2591 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId(), oldBody.getId()); |
2592 | 14ad7326 | pastith | InputStream istream = new BufferedInputStream(resourceInputStream, input); |
2593 | 14ad7326 | pastith | exception = copyRange(istream, ostream, range.start, range.end); |
2594 | 14ad7326 | pastith | // Clean up the input stream
|
2595 | 14ad7326 | pastith | istream.close(); |
2596 | 14ad7326 | pastith | // Rethrow any exception that has occurred
|
2597 | 14ad7326 | pastith | if (exception != null) |
2598 | 14ad7326 | pastith | throw exception;
|
2599 | 14ad7326 | pastith | } |
2600 | 14ad7326 | pastith | |
2601 | 14ad7326 | pastith | /**
|
2602 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2603 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2604 | 14ad7326 | pastith | * the face of an exception).
|
2605 | 14ad7326 | pastith | *
|
2606 | 14ad7326 | pastith | * @param file
|
2607 | 14ad7326 | pastith | * @param writer The writer to write to
|
2608 | 14ad7326 | pastith | * @param range Range the client wanted to retrieve
|
2609 | 14ad7326 | pastith | * @param req the HTTP request
|
2610 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2611 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
2612 | 14ad7326 | pastith | * @throws RpcException
|
2613 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
2614 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
2615 | 14ad7326 | pastith | */
|
2616 | f7c44c33 | fstamatelopoulos | protected void copy(FileHeader file, PrintWriter writer, Range range, |
2617 | f7c44c33 | fstamatelopoulos | HttpServletRequest req, FileBody oldBody) throws IOException, |
2618 | 14ad7326 | pastith | ObjectNotFoundException, InsufficientPermissionsException, RpcException { |
2619 | 14ad7326 | pastith | IOException exception = null; |
2620 | 14ad7326 | pastith | User user = getUser(req); |
2621 | 14ad7326 | pastith | InputStream resourceInputStream = oldBody == null ? |
2622 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId()) : |
2623 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId(), oldBody.getId()); |
2624 | 14ad7326 | pastith | Reader reader;
|
2625 | 14ad7326 | pastith | if (fileEncoding == null) |
2626 | 14ad7326 | pastith | reader = new InputStreamReader(resourceInputStream); |
2627 | 14ad7326 | pastith | else
|
2628 | 14ad7326 | pastith | reader = new InputStreamReader(resourceInputStream, fileEncoding); |
2629 | 14ad7326 | pastith | |
2630 | 14ad7326 | pastith | exception = copyRange(reader, writer, range.start, range.end); |
2631 | 14ad7326 | pastith | // Clean up the input stream
|
2632 | 14ad7326 | pastith | reader.close(); |
2633 | 14ad7326 | pastith | // Rethrow any exception that has occurred
|
2634 | 14ad7326 | pastith | if (exception != null) |
2635 | 14ad7326 | pastith | throw exception;
|
2636 | 14ad7326 | pastith | } |
2637 | 14ad7326 | pastith | |
2638 | 14ad7326 | pastith | /**
|
2639 | 14ad7326 | pastith | * Copy the contents of the specified input stream to the specified output
|
2640 | 14ad7326 | pastith | * stream, and ensure that both streams are closed before returning (even in
|
2641 | 14ad7326 | pastith | * the face of an exception).
|
2642 | 14ad7326 | pastith | *
|
2643 | 14ad7326 | pastith | * @param file
|
2644 | 14ad7326 | pastith | * @param ostream The output stream to write to
|
2645 | 14ad7326 | pastith | * @param ranges Enumeration of the ranges the client wanted to retrieve
|
2646 | 14ad7326 | pastith | * @param contentType Content type of the resource
|
2647 | 14ad7326 | pastith | * @param req the HTTP request
|
2648 | 14ad7326 | pastith | * @param oldBody the old version of the file, if requested
|
2649 | 14ad7326 | pastith | * @exception IOException if an input/output error occurs
|
2650 | 14ad7326 | pastith | * @throws RpcException
|
2651 | 14ad7326 | pastith | * @throws InsufficientPermissionsException
|
2652 | 14ad7326 | pastith | * @throws ObjectNotFoundException
|
2653 | 14ad7326 | pastith | */
|
2654 | f7c44c33 | fstamatelopoulos | protected void copy(FileHeader file, ServletOutputStream ostream, |
2655 | 14ad7326 | pastith | Iterator ranges, String contentType, HttpServletRequest req, |
2656 | f7c44c33 | fstamatelopoulos | FileBody oldBody) throws IOException, ObjectNotFoundException, |
2657 | 14ad7326 | pastith | InsufficientPermissionsException, RpcException { |
2658 | 14ad7326 | pastith | IOException exception = null; |
2659 | 14ad7326 | pastith | User user = getUser(req); |
2660 | 14ad7326 | pastith | while (exception == null && ranges.hasNext()) { |
2661 | 14ad7326 | pastith | InputStream resourceInputStream = oldBody == null ? |
2662 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId()) : |
2663 | 14ad7326 | pastith | getService().getFileContents(user.getId(), file.getId(), oldBody.getId()); |
2664 | 14ad7326 | pastith | InputStream istream = new BufferedInputStream(resourceInputStream, input); |
2665 | 14ad7326 | pastith | Range currentRange = (Range) ranges.next(); |
2666 | 14ad7326 | pastith | // Writing MIME header.
|
2667 | 14ad7326 | pastith | ostream.println(); |
2668 | 14ad7326 | pastith | ostream.println("--" + mimeSeparation);
|
2669 | 14ad7326 | pastith | if (contentType != null) |
2670 | 14ad7326 | pastith | ostream.println("Content-Type: " + contentType);
|
2671 | 14ad7326 | pastith | ostream.println("Content-Range: bytes " + currentRange.start + "-" + currentRange.end + "/" + currentRange.length); |
2672 | 14ad7326 | pastith | ostream.println(); |
2673 | 14ad7326 | pastith | |
2674 | 14ad7326 | pastith | // Printing content
|
2675 | 14ad7326 | pastith | exception = copyRange(istream, ostream, currentRange.start, currentRange.end); |
2676 | 14ad7326 | pastith | istream.close(); |
2677 | 14ad7326 | pastith | } |
2678 | 14ad7326 | pastith | |
2679 | 14ad7326 | pastith | ostream.println(); |
2680 | 14ad7326 | pastith | ostream.print("--" + mimeSeparation + "--"); |
2681 | 14ad7326 | pastith | // Rethrow any exception that has occurred
|
2682 | 14ad7326 | pastith | if (exception != null) |
2683 | 14ad7326 | pastith | throw exception;
|
2684 | 14ad7326 | pastith | } |
2685 | 14ad7326 | pastith | |
2686 | 14ad7326 | pastith | /**
|
2687 | 14ad7326 | pastith | * Return an InputStream to an HTML representation of the contents of this
|
2688 | 14ad7326 | pastith | * directory.
|
2689 | 14ad7326 | pastith | *
|
2690 | 14ad7326 | pastith | * @param contextPath Context path to which our internal paths are relative
|
2691 | 14ad7326 | pastith | * @param path the requested path to the resource
|
2692 | 14ad7326 | pastith | * @param folder the specified directory
|
2693 | 14ad7326 | pastith | * @param req the HTTP request
|
2694 | 14ad7326 | pastith | * @return an input stream with the rendered contents
|
2695 | 14ad7326 | pastith | * @throws IOException
|
2696 | 14ad7326 | pastith | * @throws ServletException
|
2697 | 14ad7326 | pastith | */
|
2698 | f7c44c33 | fstamatelopoulos | private InputStream renderHtml(String contextPath, String path, Folder folder, HttpServletRequest req) throws IOException, ServletException { |
2699 | 14ad7326 | pastith | String name = folder.getName();
|
2700 | 14ad7326 | pastith | // Prepare a writer to a buffered area
|
2701 | 14ad7326 | pastith | ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
2702 | 14ad7326 | pastith | OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8"); |
2703 | 14ad7326 | pastith | PrintWriter writer = new PrintWriter(osWriter); |
2704 | 14ad7326 | pastith | StringBuffer sb = new StringBuffer(); |
2705 | 14ad7326 | pastith | // rewriteUrl(contextPath) is expensive. cache result for later reuse
|
2706 | 14ad7326 | pastith | String rewrittenContextPath = rewriteUrl(contextPath);
|
2707 | 14ad7326 | pastith | // Render the page header
|
2708 | 14ad7326 | pastith | sb.append("<html>\r\n");
|
2709 | 14ad7326 | pastith | sb.append("<head>\r\n");
|
2710 | 14ad7326 | pastith | sb.append("<title>");
|
2711 | 14ad7326 | pastith | sb.append("Index of " + name);
|
2712 | 14ad7326 | pastith | sb.append("</title>\r\n");
|
2713 | 14ad7326 | pastith | sb.append("<STYLE><!--");
|
2714 | 14ad7326 | pastith | sb.append(GSS_CSS); |
2715 | 14ad7326 | pastith | sb.append("--></STYLE> ");
|
2716 | 14ad7326 | pastith | sb.append("</head>\r\n");
|
2717 | 14ad7326 | pastith | sb.append("<body>");
|
2718 | 14ad7326 | pastith | sb.append("<h1>");
|
2719 | 14ad7326 | pastith | sb.append("Index of " + name);
|
2720 | 14ad7326 | pastith | |
2721 | 14ad7326 | pastith | // Render the link to our parent (if required)
|
2722 | 14ad7326 | pastith | String parentDirectory = path;
|
2723 | 14ad7326 | pastith | if (parentDirectory.endsWith("/")) |
2724 | 14ad7326 | pastith | parentDirectory = parentDirectory.substring(0, parentDirectory.length() - 1); |
2725 | 14ad7326 | pastith | int slash = parentDirectory.lastIndexOf('/'); |
2726 | 14ad7326 | pastith | if (slash >= 0) { |
2727 | 14ad7326 | pastith | String parent = path.substring(0, slash); |
2728 | 14ad7326 | pastith | sb.append(" - <a href=\"");
|
2729 | 14ad7326 | pastith | sb.append(rewrittenContextPath); |
2730 | 14ad7326 | pastith | if (parent.equals("")) |
2731 | 14ad7326 | pastith | parent = "/";
|
2732 | 14ad7326 | pastith | sb.append(rewriteUrl(parent)); |
2733 | 14ad7326 | pastith | if (!parent.endsWith("/")) |
2734 | 14ad7326 | pastith | sb.append("/");
|
2735 | 14ad7326 | pastith | sb.append("\">");
|
2736 | 14ad7326 | pastith | sb.append("<b>");
|
2737 | 14ad7326 | pastith | sb.append("Up To " + parent);
|
2738 | 14ad7326 | pastith | sb.append("</b>");
|
2739 | 14ad7326 | pastith | sb.append("</a>");
|
2740 | 14ad7326 | pastith | } |
2741 | 14ad7326 | pastith | |
2742 | 14ad7326 | pastith | sb.append("</h1>");
|
2743 | 14ad7326 | pastith | sb.append("<HR size=\"1\" noshade=\"noshade\">");
|
2744 | 14ad7326 | pastith | |
2745 | 14ad7326 | pastith | sb.append("<table width=\"100%\" cellspacing=\"0\"" + " cellpadding=\"5\" align=\"center\">\r\n"); |
2746 | 14ad7326 | pastith | |
2747 | 14ad7326 | pastith | // Render the column headings
|
2748 | 14ad7326 | pastith | sb.append("<tr>\r\n");
|
2749 | 14ad7326 | pastith | sb.append("<td align=\"left\"><font size=\"+1\"><strong>");
|
2750 | 14ad7326 | pastith | sb.append("Name");
|
2751 | 14ad7326 | pastith | sb.append("</strong></font></td>\r\n");
|
2752 | 14ad7326 | pastith | sb.append("<td align=\"center\"><font size=\"+1\"><strong>");
|
2753 | 14ad7326 | pastith | sb.append("Size");
|
2754 | 14ad7326 | pastith | sb.append("</strong></font></td>\r\n");
|
2755 | 14ad7326 | pastith | sb.append("<td align=\"right\"><font size=\"+1\"><strong>");
|
2756 | 14ad7326 | pastith | sb.append("Last modified");
|
2757 | 14ad7326 | pastith | sb.append("</strong></font></td>\r\n");
|
2758 | 14ad7326 | pastith | sb.append("</tr>");
|
2759 | 14ad7326 | pastith | // Render the directory entries within this directory
|
2760 | 14ad7326 | pastith | boolean shade = false; |
2761 | 14ad7326 | pastith | Iterator iter = folder.getSubfolders().iterator();
|
2762 | 14ad7326 | pastith | while (iter.hasNext()) {
|
2763 | f7c44c33 | fstamatelopoulos | Folder subf = (Folder) iter.next(); |
2764 | 14ad7326 | pastith | String resourceName = subf.getName();
|
2765 | 14ad7326 | pastith | if (resourceName.equalsIgnoreCase("WEB-INF") || resourceName.equalsIgnoreCase("META-INF")) |
2766 | 14ad7326 | pastith | continue;
|
2767 | 14ad7326 | pastith | |
2768 | 14ad7326 | pastith | sb.append("<tr");
|
2769 | 14ad7326 | pastith | if (shade)
|
2770 | 14ad7326 | pastith | sb.append(" bgcolor=\"#eeeeee\"");
|
2771 | 14ad7326 | pastith | sb.append(">\r\n");
|
2772 | 14ad7326 | pastith | shade = !shade; |
2773 | 14ad7326 | pastith | |
2774 | 14ad7326 | pastith | sb.append("<td align=\"left\"> \r\n");
|
2775 | 14ad7326 | pastith | sb.append("<a href=\"");
|
2776 | 14ad7326 | pastith | sb.append(rewrittenContextPath); |
2777 | 14ad7326 | pastith | sb.append(rewriteUrl(path + resourceName)); |
2778 | 14ad7326 | pastith | sb.append("/");
|
2779 | 14ad7326 | pastith | sb.append("\"><tt>");
|
2780 | 14ad7326 | pastith | sb.append(RequestUtil.filter(resourceName)); |
2781 | 14ad7326 | pastith | sb.append("/");
|
2782 | 14ad7326 | pastith | sb.append("</tt></a></td>\r\n");
|
2783 | 14ad7326 | pastith | |
2784 | 14ad7326 | pastith | sb.append("<td align=\"right\"><tt>");
|
2785 | 14ad7326 | pastith | sb.append(" ");
|
2786 | 14ad7326 | pastith | sb.append("</tt></td>\r\n");
|
2787 | 14ad7326 | pastith | |
2788 | 14ad7326 | pastith | sb.append("<td align=\"right\"><tt>");
|
2789 | 14ad7326 | pastith | sb.append(getLastModifiedHttp(folder.getAuditInfo())); |
2790 | 14ad7326 | pastith | sb.append("</tt></td>\r\n");
|
2791 | 14ad7326 | pastith | |
2792 | 14ad7326 | pastith | sb.append("</tr>\r\n");
|
2793 | 14ad7326 | pastith | } |
2794 | f7c44c33 | fstamatelopoulos | List<FileHeader> files;
|
2795 | 14ad7326 | pastith | try {
|
2796 | 14ad7326 | pastith | User user = getUser(req); |
2797 | 3d1b9329 | koutsoub | files = getService().getFiles(user.getId(), folder.getId(), true);
|
2798 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
2799 | 14ad7326 | pastith | throw new ServletException(e.getMessage()); |
2800 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
2801 | 14ad7326 | pastith | throw new ServletException(e.getMessage()); |
2802 | 14ad7326 | pastith | } catch (RpcException e) {
|
2803 | 14ad7326 | pastith | throw new ServletException(e.getMessage()); |
2804 | 14ad7326 | pastith | } |
2805 | f7c44c33 | fstamatelopoulos | for (FileHeader fileLocal : files) {
|
2806 | f7c44c33 | fstamatelopoulos | String resourceName = fileLocal.getName();
|
2807 | 14ad7326 | pastith | if (resourceName.equalsIgnoreCase("WEB-INF") || resourceName.equalsIgnoreCase("META-INF")) |
2808 | 14ad7326 | pastith | continue;
|
2809 | 14ad7326 | pastith | |
2810 | 14ad7326 | pastith | sb.append("<tr");
|
2811 | 14ad7326 | pastith | if (shade)
|
2812 | 14ad7326 | pastith | sb.append(" bgcolor=\"#eeeeee\"");
|
2813 | 14ad7326 | pastith | sb.append(">\r\n");
|
2814 | 14ad7326 | pastith | shade = !shade; |
2815 | 14ad7326 | pastith | |
2816 | 14ad7326 | pastith | sb.append("<td align=\"left\"> \r\n");
|
2817 | 14ad7326 | pastith | sb.append("<a href=\"");
|
2818 | 14ad7326 | pastith | sb.append(rewrittenContextPath); |
2819 | 14ad7326 | pastith | sb.append(rewriteUrl(path + resourceName)); |
2820 | 14ad7326 | pastith | sb.append("\"><tt>");
|
2821 | 14ad7326 | pastith | sb.append(RequestUtil.filter(resourceName)); |
2822 | 14ad7326 | pastith | sb.append("</tt></a></td>\r\n");
|
2823 | 14ad7326 | pastith | |
2824 | 14ad7326 | pastith | sb.append("<td align=\"right\"><tt>");
|
2825 | f7c44c33 | fstamatelopoulos | sb.append(renderSize(fileLocal.getCurrentBody().getFileSize())); |
2826 | 14ad7326 | pastith | sb.append("</tt></td>\r\n");
|
2827 | 14ad7326 | pastith | |
2828 | 14ad7326 | pastith | sb.append("<td align=\"right\"><tt>");
|
2829 | f7c44c33 | fstamatelopoulos | sb.append(getLastModifiedHttp(fileLocal.getAuditInfo())); |
2830 | 14ad7326 | pastith | sb.append("</tt></td>\r\n");
|
2831 | 14ad7326 | pastith | |
2832 | 14ad7326 | pastith | sb.append("</tr>\r\n");
|
2833 | 14ad7326 | pastith | } |
2834 | 14ad7326 | pastith | |
2835 | 14ad7326 | pastith | // Render the page footer
|
2836 | 14ad7326 | pastith | sb.append("</table>\r\n");
|
2837 | 14ad7326 | pastith | |
2838 | 14ad7326 | pastith | sb.append("<HR size=\"1\" noshade=\"noshade\">");
|
2839 | 14ad7326 | pastith | |
2840 | 14ad7326 | pastith | sb.append("<h3>").append(getServletContext().getServerInfo()).append("</h3>"); |
2841 | 14ad7326 | pastith | sb.append("</body>\r\n");
|
2842 | 14ad7326 | pastith | sb.append("</html>\r\n");
|
2843 | 14ad7326 | pastith | |
2844 | 14ad7326 | pastith | // Return an input stream to the underlying bytes
|
2845 | 14ad7326 | pastith | writer.write(sb.toString()); |
2846 | 14ad7326 | pastith | writer.flush(); |
2847 | 14ad7326 | pastith | return new ByteArrayInputStream(stream.toByteArray()); |
2848 | 14ad7326 | pastith | |
2849 | 14ad7326 | pastith | } |
2850 | 14ad7326 | pastith | |
2851 | 14ad7326 | pastith | /**
|
2852 | 14ad7326 | pastith | * Render the specified file size (in bytes).
|
2853 | 14ad7326 | pastith | *
|
2854 | 14ad7326 | pastith | * @param size File size (in bytes)
|
2855 | 14ad7326 | pastith | * @return the size as a string
|
2856 | 14ad7326 | pastith | */
|
2857 | 31e19588 | Natasa Kapravelou | protected String renderSize(long size) { |
2858 | 14ad7326 | pastith | long leftSide = size / 1024; |
2859 | 14ad7326 | pastith | long rightSide = size % 1024 / 103; // Makes 1 digit |
2860 | 14ad7326 | pastith | if (leftSide == 0 && rightSide == 0 && size > 0) |
2861 | 14ad7326 | pastith | rightSide = 1;
|
2862 | 14ad7326 | pastith | return "" + leftSide + "." + rightSide + " kb"; |
2863 | 14ad7326 | pastith | } |
2864 | 14ad7326 | pastith | |
2865 | 14ad7326 | pastith | /**
|
2866 | 14ad7326 | pastith | * Copy a resource.
|
2867 | 14ad7326 | pastith | *
|
2868 | 14ad7326 | pastith | * @param req Servlet request
|
2869 | 14ad7326 | pastith | * @param resp Servlet response
|
2870 | 14ad7326 | pastith | * @return boolean true if the copy is successful
|
2871 | 14ad7326 | pastith | * @throws IOException
|
2872 | 14ad7326 | pastith | */
|
2873 | 14ad7326 | pastith | private boolean copyResource(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
2874 | 14ad7326 | pastith | // Parsing destination header
|
2875 | 14ad7326 | pastith | String destinationPath = req.getHeader("Destination"); |
2876 | 14ad7326 | pastith | if (destinationPath == null) { |
2877 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_BAD_REQUEST); |
2878 | 14ad7326 | pastith | return false; |
2879 | 14ad7326 | pastith | } |
2880 | 14ad7326 | pastith | |
2881 | 14ad7326 | pastith | // Remove url encoding from destination
|
2882 | 14ad7326 | pastith | destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
|
2883 | 14ad7326 | pastith | |
2884 | 14ad7326 | pastith | int protocolIndex = destinationPath.indexOf("://"); |
2885 | 14ad7326 | pastith | if (protocolIndex >= 0) { |
2886 | 14ad7326 | pastith | // if the Destination URL contains the protocol, we can safely
|
2887 | 14ad7326 | pastith | // trim everything upto the first "/" character after "://"
|
2888 | 14ad7326 | pastith | int firstSeparator = destinationPath.indexOf("/", protocolIndex + 4); |
2889 | 14ad7326 | pastith | if (firstSeparator < 0) |
2890 | 14ad7326 | pastith | destinationPath = "/";
|
2891 | 14ad7326 | pastith | else
|
2892 | 14ad7326 | pastith | destinationPath = destinationPath.substring(firstSeparator); |
2893 | 14ad7326 | pastith | } else {
|
2894 | 14ad7326 | pastith | String hostName = req.getServerName();
|
2895 | 14ad7326 | pastith | if (hostName != null && destinationPath.startsWith(hostName)) |
2896 | 14ad7326 | pastith | destinationPath = destinationPath.substring(hostName.length()); |
2897 | 14ad7326 | pastith | |
2898 | 14ad7326 | pastith | int portIndex = destinationPath.indexOf(":"); |
2899 | 14ad7326 | pastith | if (portIndex >= 0) |
2900 | 14ad7326 | pastith | destinationPath = destinationPath.substring(portIndex); |
2901 | 14ad7326 | pastith | |
2902 | 14ad7326 | pastith | if (destinationPath.startsWith(":")) { |
2903 | 14ad7326 | pastith | int firstSeparator = destinationPath.indexOf("/"); |
2904 | 14ad7326 | pastith | if (firstSeparator < 0) |
2905 | 14ad7326 | pastith | destinationPath = "/";
|
2906 | 14ad7326 | pastith | else
|
2907 | 14ad7326 | pastith | destinationPath = destinationPath.substring(firstSeparator); |
2908 | 14ad7326 | pastith | } |
2909 | 14ad7326 | pastith | } |
2910 | 14ad7326 | pastith | |
2911 | 5416cc9e | pastith | // Normalize destination path (remove '.' and '..')
|
2912 | 5416cc9e | pastith | destinationPath = RequestUtil.normalize(destinationPath); |
2913 | 14ad7326 | pastith | |
2914 | 14ad7326 | pastith | String contextPath = req.getContextPath();
|
2915 | 14ad7326 | pastith | if (contextPath != null && destinationPath.startsWith(contextPath)) |
2916 | 14ad7326 | pastith | destinationPath = destinationPath.substring(contextPath.length()); |
2917 | 14ad7326 | pastith | |
2918 | 14ad7326 | pastith | String pathInfo = req.getPathInfo();
|
2919 | 14ad7326 | pastith | if (pathInfo != null) { |
2920 | 14ad7326 | pastith | String servletPath = req.getServletPath();
|
2921 | 14ad7326 | pastith | if (servletPath != null && destinationPath.startsWith(servletPath)) |
2922 | 14ad7326 | pastith | destinationPath = destinationPath.substring(servletPath.length()); |
2923 | 14ad7326 | pastith | } |
2924 | 14ad7326 | pastith | |
2925 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
2926 | 14ad7326 | pastith | logger.debug("Dest path :" + destinationPath);
|
2927 | 14ad7326 | pastith | |
2928 | 14ad7326 | pastith | if (destinationPath.toUpperCase().startsWith("/WEB-INF") || destinationPath.toUpperCase().startsWith("/META-INF")) { |
2929 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN); |
2930 | 14ad7326 | pastith | return false; |
2931 | 14ad7326 | pastith | } |
2932 | 14ad7326 | pastith | |
2933 | 14ad7326 | pastith | String path = getRelativePath(req);
|
2934 | 14ad7326 | pastith | |
2935 | 14ad7326 | pastith | if (path.toUpperCase().startsWith("/WEB-INF") || path.toUpperCase().startsWith("/META-INF")) { |
2936 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN); |
2937 | 14ad7326 | pastith | return false; |
2938 | 14ad7326 | pastith | } |
2939 | 14ad7326 | pastith | |
2940 | 14ad7326 | pastith | if (destinationPath.equals(path)) {
|
2941 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN); |
2942 | 14ad7326 | pastith | return false; |
2943 | 14ad7326 | pastith | } |
2944 | 14ad7326 | pastith | |
2945 | 14ad7326 | pastith | // Parsing overwrite header
|
2946 | 14ad7326 | pastith | boolean overwrite = true; |
2947 | 14ad7326 | pastith | String overwriteHeader = req.getHeader("Overwrite"); |
2948 | 14ad7326 | pastith | |
2949 | 14ad7326 | pastith | if (overwriteHeader != null) |
2950 | 14ad7326 | pastith | if (overwriteHeader.equalsIgnoreCase("T")) |
2951 | 14ad7326 | pastith | overwrite = true;
|
2952 | 14ad7326 | pastith | else
|
2953 | 14ad7326 | pastith | overwrite = false;
|
2954 | 14ad7326 | pastith | |
2955 | 14ad7326 | pastith | User user = getUser(req); |
2956 | 14ad7326 | pastith | // Overwriting the destination
|
2957 | 14ad7326 | pastith | boolean exists = true; |
2958 | 14ad7326 | pastith | try {
|
2959 | 68410d59 | pastith | getService().getResourceAtPath(user.getId(), destinationPath, true);
|
2960 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
2961 | 14ad7326 | pastith | exists = false;
|
2962 | 14ad7326 | pastith | } catch (RpcException e) {
|
2963 | 14ad7326 | pastith | resp.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR); |
2964 | 14ad7326 | pastith | return false; |
2965 | 14ad7326 | pastith | } |
2966 | 14ad7326 | pastith | |
2967 | 14ad7326 | pastith | if (overwrite) {
|
2968 | 14ad7326 | pastith | // Delete destination resource, if it exists
|
2969 | 14ad7326 | pastith | if (exists) {
|
2970 | 14ad7326 | pastith | if (!deleteResource(destinationPath, req, resp, true)) |
2971 | 14ad7326 | pastith | return false; |
2972 | 14ad7326 | pastith | } else
|
2973 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_CREATED); |
2974 | 14ad7326 | pastith | } else // If the destination exists, then it's a conflict |
2975 | 14ad7326 | pastith | if (exists) {
|
2976 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED); |
2977 | 14ad7326 | pastith | return false; |
2978 | 14ad7326 | pastith | } else
|
2979 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_CREATED); |
2980 | 14ad7326 | pastith | |
2981 | 14ad7326 | pastith | // Copying source to destination.
|
2982 | 14ad7326 | pastith | Hashtable<String, Integer> errorList = new Hashtable<String, Integer>(); |
2983 | 14ad7326 | pastith | boolean result;
|
2984 | 14ad7326 | pastith | try {
|
2985 | 14ad7326 | pastith | result = copyResource(errorList, path, destinationPath, req); |
2986 | 14ad7326 | pastith | } catch (RpcException e) {
|
2987 | 14ad7326 | pastith | resp.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR); |
2988 | 14ad7326 | pastith | return false; |
2989 | 14ad7326 | pastith | } |
2990 | 14ad7326 | pastith | if (!result || !errorList.isEmpty()) {
|
2991 | 14ad7326 | pastith | sendReport(req, resp, errorList); |
2992 | 14ad7326 | pastith | return false; |
2993 | 14ad7326 | pastith | } |
2994 | 14ad7326 | pastith | return true; |
2995 | 14ad7326 | pastith | } |
2996 | 14ad7326 | pastith | |
2997 | 14ad7326 | pastith | /**
|
2998 | 14ad7326 | pastith | * Copy a collection.
|
2999 | 14ad7326 | pastith | *
|
3000 | 14ad7326 | pastith | * @param errorList Hashtable containing the list of errors which occurred
|
3001 | 14ad7326 | pastith | * during the copy operation
|
3002 | 14ad7326 | pastith | * @param source Path of the resource to be copied
|
3003 | 14ad7326 | pastith | * @param theDest Destination path
|
3004 | 14ad7326 | pastith | * @param req the HTTP request
|
3005 | 14ad7326 | pastith | * @return boolean true if the copy is successful
|
3006 | 14ad7326 | pastith | * @throws RpcException
|
3007 | 14ad7326 | pastith | */
|
3008 | 14ad7326 | pastith | private boolean copyResource(Hashtable<String, Integer> errorList, String source, String theDest, HttpServletRequest req) throws RpcException { |
3009 | 14ad7326 | pastith | |
3010 | 14ad7326 | pastith | String dest = theDest;
|
3011 | 14ad7326 | pastith | // Fix the destination path when copying collections.
|
3012 | 14ad7326 | pastith | if (source.endsWith("/") && !dest.endsWith("/")) |
3013 | 14ad7326 | pastith | dest += "/";
|
3014 | 14ad7326 | pastith | |
3015 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
3016 | 14ad7326 | pastith | logger.debug("Copy: " + source + " To: " + dest); |
3017 | 14ad7326 | pastith | |
3018 | 3b6b7f25 | Dimitris Routsis | final User user = getUser(req);
|
3019 | 14ad7326 | pastith | Object object = null; |
3020 | 14ad7326 | pastith | try {
|
3021 | 68410d59 | pastith | object = getService().getResourceAtPath(user.getId(), source, true);
|
3022 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3023 | 14ad7326 | pastith | } |
3024 | 14ad7326 | pastith | |
3025 | f7c44c33 | fstamatelopoulos | if (object instanceof Folder) { |
3026 | f7c44c33 | fstamatelopoulos | final Folder folderLocal = (Folder) object;
|
3027 | 14ad7326 | pastith | try {
|
3028 | 3b6b7f25 | Dimitris Routsis | final String des = dest; |
3029 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
3030 | 3b6b7f25 | Dimitris Routsis | @Override
|
3031 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
3032 | f7c44c33 | fstamatelopoulos | getService().copyFolder(user.getId(), folderLocal.getId(), des); |
3033 | 3b6b7f25 | Dimitris Routsis | return null; |
3034 | 3b6b7f25 | Dimitris Routsis | } |
3035 | 3b6b7f25 | Dimitris Routsis | }); |
3036 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3037 | 14ad7326 | pastith | errorList.put(dest, new Integer(WebdavStatus.SC_CONFLICT)); |
3038 | 14ad7326 | pastith | return false; |
3039 | 14ad7326 | pastith | } catch (DuplicateNameException e) {
|
3040 | 14ad7326 | pastith | errorList.put(dest, new Integer(WebdavStatus.SC_CONFLICT)); |
3041 | 14ad7326 | pastith | return false; |
3042 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
3043 | 14ad7326 | pastith | errorList.put(dest, new Integer(WebdavStatus.SC_FORBIDDEN)); |
3044 | 14ad7326 | pastith | return false; |
3045 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
3046 | 3b6b7f25 | Dimitris Routsis | errorList.put(dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3047 | 3b6b7f25 | Dimitris Routsis | return false; |
3048 | 14ad7326 | pastith | } |
3049 | 14ad7326 | pastith | |
3050 | 14ad7326 | pastith | try {
|
3051 | 14ad7326 | pastith | String newSource = source;
|
3052 | 14ad7326 | pastith | if (!source.endsWith("/")) |
3053 | 14ad7326 | pastith | newSource += "/";
|
3054 | 14ad7326 | pastith | String newDest = dest;
|
3055 | 14ad7326 | pastith | if (!dest.endsWith("/")) |
3056 | 14ad7326 | pastith | newDest += "/";
|
3057 | 14ad7326 | pastith | // Recursively copy the subfolders.
|
3058 | f7c44c33 | fstamatelopoulos | Iterator iter = folderLocal.getSubfolders().iterator();
|
3059 | 14ad7326 | pastith | while (iter.hasNext()) {
|
3060 | f7c44c33 | fstamatelopoulos | Folder subf = (Folder) iter.next(); |
3061 | 14ad7326 | pastith | String resourceName = subf.getName();
|
3062 | 14ad7326 | pastith | copyResource(errorList, newSource + resourceName, newDest + resourceName, req); |
3063 | 14ad7326 | pastith | } |
3064 | 14ad7326 | pastith | // Recursively copy the files.
|
3065 | f7c44c33 | fstamatelopoulos | List<FileHeader> files;
|
3066 | f7c44c33 | fstamatelopoulos | files = getService().getFiles(user.getId(), folderLocal.getId(), true);
|
3067 | f7c44c33 | fstamatelopoulos | for (FileHeader file : files) {
|
3068 | 14ad7326 | pastith | String resourceName = file.getName();
|
3069 | 14ad7326 | pastith | copyResource(errorList, newSource + resourceName, newDest + resourceName, req); |
3070 | 14ad7326 | pastith | } |
3071 | 14ad7326 | pastith | } catch (RpcException e) {
|
3072 | 14ad7326 | pastith | errorList.put(dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3073 | 14ad7326 | pastith | return false; |
3074 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3075 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_NOT_FOUND)); |
3076 | 14ad7326 | pastith | return false; |
3077 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
3078 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_FORBIDDEN)); |
3079 | 14ad7326 | pastith | return false; |
3080 | 14ad7326 | pastith | } |
3081 | 14ad7326 | pastith | |
3082 | f7c44c33 | fstamatelopoulos | } else if (object instanceof FileHeader) { |
3083 | f7c44c33 | fstamatelopoulos | final FileHeader fileLocal = (FileHeader) object;
|
3084 | 14ad7326 | pastith | try {
|
3085 | 3b6b7f25 | Dimitris Routsis | final String des = dest; |
3086 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
3087 | 3b6b7f25 | Dimitris Routsis | @Override
|
3088 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
3089 | f7c44c33 | fstamatelopoulos | getService().copyFile(user.getId(), fileLocal.getId(), des); |
3090 | 3b6b7f25 | Dimitris Routsis | return null; |
3091 | 3b6b7f25 | Dimitris Routsis | } |
3092 | 3b6b7f25 | Dimitris Routsis | }); |
3093 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3094 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3095 | 14ad7326 | pastith | return false; |
3096 | 14ad7326 | pastith | } catch (DuplicateNameException e) {
|
3097 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_CONFLICT)); |
3098 | 14ad7326 | pastith | return false; |
3099 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
3100 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_FORBIDDEN)); |
3101 | 14ad7326 | pastith | return false; |
3102 | 14ad7326 | pastith | } catch (QuotaExceededException e) {
|
3103 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_FORBIDDEN)); |
3104 | 14ad7326 | pastith | return false; |
3105 | 14ad7326 | pastith | } catch (GSSIOException e) {
|
3106 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3107 | 14ad7326 | pastith | return false; |
3108 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
3109 | 3b6b7f25 | Dimitris Routsis | errorList.put(dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3110 | 3b6b7f25 | Dimitris Routsis | return false; |
3111 | 14ad7326 | pastith | } |
3112 | 14ad7326 | pastith | } else {
|
3113 | 14ad7326 | pastith | errorList.put(source, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3114 | 14ad7326 | pastith | return false; |
3115 | 14ad7326 | pastith | } |
3116 | 14ad7326 | pastith | return true; |
3117 | 14ad7326 | pastith | } |
3118 | 14ad7326 | pastith | |
3119 | 14ad7326 | pastith | /**
|
3120 | 14ad7326 | pastith | * Delete a resource.
|
3121 | 14ad7326 | pastith | *
|
3122 | 14ad7326 | pastith | * @param req Servlet request
|
3123 | 14ad7326 | pastith | * @param resp Servlet response
|
3124 | 14ad7326 | pastith | * @return boolean true if the deletion is successful
|
3125 | 14ad7326 | pastith | * @throws IOException
|
3126 | 14ad7326 | pastith | */
|
3127 | 14ad7326 | pastith | private boolean deleteResource(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
3128 | 14ad7326 | pastith | String path = getRelativePath(req);
|
3129 | 14ad7326 | pastith | return deleteResource(path, req, resp, true); |
3130 | 14ad7326 | pastith | } |
3131 | 14ad7326 | pastith | |
3132 | 14ad7326 | pastith | /**
|
3133 | 14ad7326 | pastith | * Delete a resource.
|
3134 | 14ad7326 | pastith | *
|
3135 | 14ad7326 | pastith | * @param path Path of the resource which is to be deleted
|
3136 | 14ad7326 | pastith | * @param req Servlet request
|
3137 | 14ad7326 | pastith | * @param resp Servlet response
|
3138 | 14ad7326 | pastith | * @param setStatus Should the response status be set on successful
|
3139 | 14ad7326 | pastith | * completion
|
3140 | 14ad7326 | pastith | * @return boolean true if the deletion is successful
|
3141 | 14ad7326 | pastith | * @throws IOException
|
3142 | 14ad7326 | pastith | */
|
3143 | 14ad7326 | pastith | private boolean deleteResource(String path, HttpServletRequest req, HttpServletResponse resp, boolean setStatus) throws IOException { |
3144 | 14ad7326 | pastith | if (path.toUpperCase().startsWith("/WEB-INF") || path.toUpperCase().startsWith("/META-INF")) { |
3145 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_FORBIDDEN); |
3146 | 14ad7326 | pastith | return false; |
3147 | 14ad7326 | pastith | } |
3148 | 14ad7326 | pastith | String ifHeader = req.getHeader("If"); |
3149 | 14ad7326 | pastith | if (ifHeader == null) |
3150 | 14ad7326 | pastith | ifHeader = "";
|
3151 | 14ad7326 | pastith | |
3152 | 14ad7326 | pastith | String lockTokenHeader = req.getHeader("Lock-Token"); |
3153 | 14ad7326 | pastith | if (lockTokenHeader == null) |
3154 | 14ad7326 | pastith | lockTokenHeader = "";
|
3155 | 14ad7326 | pastith | |
3156 | 14ad7326 | pastith | if (isLocked(path, ifHeader + lockTokenHeader)) {
|
3157 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_LOCKED); |
3158 | 14ad7326 | pastith | return false; |
3159 | 14ad7326 | pastith | } |
3160 | 14ad7326 | pastith | |
3161 | 3b6b7f25 | Dimitris Routsis | final User user = getUser(req);
|
3162 | 14ad7326 | pastith | boolean exists = true; |
3163 | 14ad7326 | pastith | Object object = null; |
3164 | 14ad7326 | pastith | try {
|
3165 | 68410d59 | pastith | object = getService().getResourceAtPath(user.getId(), path, true);
|
3166 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3167 | 14ad7326 | pastith | exists = false;
|
3168 | 14ad7326 | pastith | } catch (RpcException e) {
|
3169 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); |
3170 | 14ad7326 | pastith | return false; |
3171 | 14ad7326 | pastith | } |
3172 | 14ad7326 | pastith | |
3173 | 14ad7326 | pastith | if (!exists) {
|
3174 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_NOT_FOUND); |
3175 | 14ad7326 | pastith | return false; |
3176 | 14ad7326 | pastith | } |
3177 | 14ad7326 | pastith | |
3178 | f7c44c33 | fstamatelopoulos | Folder folderLocal = null;
|
3179 | f7c44c33 | fstamatelopoulos | FileHeader fileLocal = null;
|
3180 | f7c44c33 | fstamatelopoulos | if (object instanceof Folder) |
3181 | f7c44c33 | fstamatelopoulos | folderLocal = (Folder) object; |
3182 | 14ad7326 | pastith | else
|
3183 | f7c44c33 | fstamatelopoulos | fileLocal = (FileHeader) object; |
3184 | 14ad7326 | pastith | |
3185 | f7c44c33 | fstamatelopoulos | if (fileLocal != null) |
3186 | 14ad7326 | pastith | try {
|
3187 | f7c44c33 | fstamatelopoulos | final FileHeader f = fileLocal;
|
3188 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
3189 | 3b6b7f25 | Dimitris Routsis | @Override
|
3190 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
3191 | 3b6b7f25 | Dimitris Routsis | getService().deleteFile(user.getId(), f.getId()); |
3192 | 3b6b7f25 | Dimitris Routsis | return null; |
3193 | 3b6b7f25 | Dimitris Routsis | } |
3194 | 3b6b7f25 | Dimitris Routsis | }); |
3195 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
3196 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); |
3197 | 14ad7326 | pastith | return false; |
3198 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3199 | 14ad7326 | pastith | // Although we had already found the object, it was
|
3200 | 14ad7326 | pastith | // probably deleted from another thread.
|
3201 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_NOT_FOUND); |
3202 | 14ad7326 | pastith | return false; |
3203 | 14ad7326 | pastith | } catch (RpcException e) {
|
3204 | 14ad7326 | pastith | resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); |
3205 | 14ad7326 | pastith | return false; |
3206 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
3207 | 3b6b7f25 | Dimitris Routsis | resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR); |
3208 | 3b6b7f25 | Dimitris Routsis | return false; |
3209 | 14ad7326 | pastith | } |
3210 | f7c44c33 | fstamatelopoulos | else if (folderLocal != null) { |
3211 | 14ad7326 | pastith | Hashtable<String, Integer> errorList = new Hashtable<String, Integer>(); |
3212 | f7c44c33 | fstamatelopoulos | deleteCollection(req, folderLocal, path, errorList); |
3213 | 14ad7326 | pastith | try {
|
3214 | f7c44c33 | fstamatelopoulos | final Folder f = folderLocal;
|
3215 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
3216 | 3b6b7f25 | Dimitris Routsis | @Override
|
3217 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
3218 | 3b6b7f25 | Dimitris Routsis | getService().deleteFolder(user.getId(), f.getId()); |
3219 | 3b6b7f25 | Dimitris Routsis | return null; |
3220 | 3b6b7f25 | Dimitris Routsis | } |
3221 | 3b6b7f25 | Dimitris Routsis | }); |
3222 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
3223 | 14ad7326 | pastith | errorList.put(path, new Integer(WebdavStatus.SC_METHOD_NOT_ALLOWED)); |
3224 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3225 | 14ad7326 | pastith | errorList.put(path, new Integer(WebdavStatus.SC_NOT_FOUND)); |
3226 | 14ad7326 | pastith | } catch (RpcException e) {
|
3227 | 14ad7326 | pastith | errorList.put(path, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3228 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
3229 | 3b6b7f25 | Dimitris Routsis | errorList.put(path, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3230 | 14ad7326 | pastith | } |
3231 | 14ad7326 | pastith | |
3232 | 14ad7326 | pastith | if (!errorList.isEmpty()) {
|
3233 | 14ad7326 | pastith | sendReport(req, resp, errorList); |
3234 | 14ad7326 | pastith | return false; |
3235 | 14ad7326 | pastith | } |
3236 | 14ad7326 | pastith | } |
3237 | 14ad7326 | pastith | if (setStatus)
|
3238 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_NO_CONTENT); |
3239 | 14ad7326 | pastith | return true; |
3240 | 14ad7326 | pastith | } |
3241 | 14ad7326 | pastith | |
3242 | 14ad7326 | pastith | /**
|
3243 | 14ad7326 | pastith | * Deletes a collection.
|
3244 | 14ad7326 | pastith | *
|
3245 | 14ad7326 | pastith | * @param req the HTTP request
|
3246 | 14ad7326 | pastith | * @param folder the folder whose contents will be deleted
|
3247 | 14ad7326 | pastith | * @param path Path to the collection to be deleted
|
3248 | 14ad7326 | pastith | * @param errorList Contains the list of the errors which occurred
|
3249 | 14ad7326 | pastith | */
|
3250 | f7c44c33 | fstamatelopoulos | private void deleteCollection(HttpServletRequest req, Folder folder, String path, Hashtable<String, Integer> errorList) { |
3251 | 14ad7326 | pastith | |
3252 | 14ad7326 | pastith | if (logger.isDebugEnabled())
|
3253 | 14ad7326 | pastith | logger.debug("Delete:" + path);
|
3254 | 14ad7326 | pastith | |
3255 | 14ad7326 | pastith | if (path.toUpperCase().startsWith("/WEB-INF") || path.toUpperCase().startsWith("/META-INF")) { |
3256 | 14ad7326 | pastith | errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN)); |
3257 | 14ad7326 | pastith | return;
|
3258 | 14ad7326 | pastith | } |
3259 | 14ad7326 | pastith | |
3260 | 14ad7326 | pastith | String ifHeader = req.getHeader("If"); |
3261 | 14ad7326 | pastith | if (ifHeader == null) |
3262 | 14ad7326 | pastith | ifHeader = "";
|
3263 | 14ad7326 | pastith | |
3264 | 14ad7326 | pastith | String lockTokenHeader = req.getHeader("Lock-Token"); |
3265 | 14ad7326 | pastith | if (lockTokenHeader == null) |
3266 | 14ad7326 | pastith | lockTokenHeader = "";
|
3267 | 14ad7326 | pastith | |
3268 | 14ad7326 | pastith | Iterator iter = folder.getSubfolders().iterator();
|
3269 | 14ad7326 | pastith | while (iter.hasNext()) {
|
3270 | f7c44c33 | fstamatelopoulos | Folder subf = (Folder) iter.next(); |
3271 | 14ad7326 | pastith | String childName = path;
|
3272 | 14ad7326 | pastith | if (!childName.equals("/")) |
3273 | 14ad7326 | pastith | childName += "/";
|
3274 | 14ad7326 | pastith | childName += subf.getName(); |
3275 | 14ad7326 | pastith | |
3276 | 14ad7326 | pastith | if (isLocked(childName, ifHeader + lockTokenHeader))
|
3277 | 14ad7326 | pastith | errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED)); |
3278 | 14ad7326 | pastith | else
|
3279 | 14ad7326 | pastith | try {
|
3280 | 3b6b7f25 | Dimitris Routsis | final User user = getUser(req);
|
3281 | 68410d59 | pastith | Object object = getService().getResourceAtPath(user.getId(), childName, true); |
3282 | f7c44c33 | fstamatelopoulos | Folder childFolder = null;
|
3283 | f7c44c33 | fstamatelopoulos | FileHeader childFile = null;
|
3284 | f7c44c33 | fstamatelopoulos | if (object instanceof Folder) |
3285 | f7c44c33 | fstamatelopoulos | childFolder = (Folder) object; |
3286 | 14ad7326 | pastith | else
|
3287 | f7c44c33 | fstamatelopoulos | childFile = (FileHeader) object; |
3288 | 14ad7326 | pastith | if (childFolder != null) { |
3289 | f7c44c33 | fstamatelopoulos | final Folder cf = childFolder;
|
3290 | 14ad7326 | pastith | deleteCollection(req, childFolder, childName, errorList); |
3291 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
3292 | 3b6b7f25 | Dimitris Routsis | @Override
|
3293 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
3294 | 3b6b7f25 | Dimitris Routsis | getService().deleteFolder(user.getId(), cf.getId()); |
3295 | 3b6b7f25 | Dimitris Routsis | return null; |
3296 | 3b6b7f25 | Dimitris Routsis | } |
3297 | 3b6b7f25 | Dimitris Routsis | }); |
3298 | 3b6b7f25 | Dimitris Routsis | } else if (childFile != null) { |
3299 | f7c44c33 | fstamatelopoulos | final FileHeader cf = childFile;
|
3300 | 3b6b7f25 | Dimitris Routsis | new TransactionHelper<Void>().tryExecute(new Callable<Void>() { |
3301 | 3b6b7f25 | Dimitris Routsis | @Override
|
3302 | 3b6b7f25 | Dimitris Routsis | public Void call() throws Exception { |
3303 | 3b6b7f25 | Dimitris Routsis | getService().deleteFile(user.getId(), cf.getId()); |
3304 | 3b6b7f25 | Dimitris Routsis | return null; |
3305 | 3b6b7f25 | Dimitris Routsis | } |
3306 | 3b6b7f25 | Dimitris Routsis | }); |
3307 | 3b6b7f25 | Dimitris Routsis | } |
3308 | 14ad7326 | pastith | } catch (ObjectNotFoundException e) {
|
3309 | 14ad7326 | pastith | errorList.put(childName, new Integer(WebdavStatus.SC_NOT_FOUND)); |
3310 | 14ad7326 | pastith | } catch (InsufficientPermissionsException e) {
|
3311 | 14ad7326 | pastith | errorList.put(childName, new Integer(WebdavStatus.SC_FORBIDDEN)); |
3312 | 14ad7326 | pastith | } catch (RpcException e) {
|
3313 | 14ad7326 | pastith | errorList.put(childName, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3314 | 3b6b7f25 | Dimitris Routsis | } catch (Exception e) { |
3315 | 3b6b7f25 | Dimitris Routsis | errorList.put(childName, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); |
3316 | 14ad7326 | pastith | } |
3317 | 14ad7326 | pastith | } |
3318 | 14ad7326 | pastith | } |
3319 | 14ad7326 | pastith | |
3320 | 14ad7326 | pastith | /**
|
3321 | 14ad7326 | pastith | * Send a multistatus element containing a complete error report to the
|
3322 | 14ad7326 | pastith | * client.
|
3323 | 14ad7326 | pastith | *
|
3324 | 14ad7326 | pastith | * @param req Servlet request
|
3325 | 14ad7326 | pastith | * @param resp Servlet response
|
3326 | 14ad7326 | pastith | * @param errorList List of error to be displayed
|
3327 | 14ad7326 | pastith | * @throws IOException
|
3328 | 14ad7326 | pastith | */
|
3329 | 14ad7326 | pastith | private void sendReport(HttpServletRequest req, HttpServletResponse resp, Hashtable errorList) throws IOException { |
3330 | 14ad7326 | pastith | |
3331 | 14ad7326 | pastith | resp.setStatus(WebdavStatus.SC_MULTI_STATUS); |
3332 | 14ad7326 | pastith | |
3333 | 14ad7326 | pastith | String absoluteUri = req.getRequestURI();
|
3334 | 14ad7326 | pastith | String relativePath = getRelativePath(req);
|
3335 | 14ad7326 | pastith | |
3336 | 14ad7326 | pastith | XMLWriter generatedXML = new XMLWriter();
|
3337 | 14ad7326 | pastith | generatedXML.writeXMLHeader(); |
3338 | 14ad7326 | pastith | |
3339 | 14ad7326 | pastith | generatedXML.writeElement(null, "multistatus" + generateNamespaceDeclarations(), XMLWriter.OPENING); |
3340 | 14ad7326 | pastith | |
3341 | 14ad7326 | pastith | Enumeration pathList = errorList.keys();
|
3342 | 14ad7326 | pastith | while (pathList.hasMoreElements()) {
|
3343 | 14ad7326 | pastith | |
3344 | 14ad7326 | pastith | String errorPath = (String) pathList.nextElement(); |
3345 | 14ad7326 | pastith | int errorCode = ((Integer) errorList.get(errorPath)).intValue(); |
3346 | 14ad7326 | pastith | |
3347 | 14ad7326 | pastith | generatedXML.writeElement(null, "response", XMLWriter.OPENING); |
3348 | 14ad7326 | pastith | |
3349 | 14ad7326 | pastith | generatedXML.writeElement(null, "href", XMLWriter.OPENING); |
3350 | 14ad7326 | pastith | String toAppend = errorPath.substring(relativePath.length());
|
3351 | 14ad7326 | pastith | if (!toAppend.startsWith("/")) |
3352 | 14ad7326 | pastith | toAppend = "/" + toAppend;
|
3353 | 14ad7326 | pastith | generatedXML.writeText(absoluteUri + toAppend); |
3354 | 14ad7326 | pastith | generatedXML.writeElement(null, "href", XMLWriter.CLOSING); |
3355 | 14ad7326 | pastith | generatedXML.writeElement(null, "status", XMLWriter.OPENING); |
3356 | 14ad7326 | pastith | generatedXML.writeText("HTTP/1.1 " + errorCode + " " + WebdavStatus.getStatusText(errorCode)); |
3357 | 14ad7326 | pastith | generatedXML.writeElement(null, "status", XMLWriter.CLOSING); |
3358 | 14ad7326 | pastith | |
3359 | 14ad7326 | pastith | generatedXML.writeElement(null, "response", XMLWriter.CLOSING); |
3360 | 14ad7326 | pastith | |
3361 | 14ad7326 | pastith | } |
3362 | 14ad7326 | pastith | |
3363 | 14ad7326 | pastith | generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING); |
3364 | 14ad7326 | pastith | |
3365 | 14ad7326 | pastith | Writer writer = resp.getWriter();
|
3366 | 14ad7326 | pastith | writer.write(generatedXML.toString()); |
3367 | 14ad7326 | pastith | writer.close(); |
3368 | 14ad7326 | pastith | |
3369 | 14ad7326 | pastith | } |
3370 | 14ad7326 | pastith | |
3371 | 14ad7326 | pastith | // --------------------------------------------- WebdavResolver Inner Class
|
3372 | 14ad7326 | pastith | /**
|
3373 | 14ad7326 | pastith | * Work around for XML parsers that don't fully respect
|
3374 | 14ad7326 | pastith | * {@link DocumentBuilderFactory#setExpandEntityReferences(boolean)}.
|
3375 | 14ad7326 | pastith | * External references are filtered out for security reasons. See
|
3376 | 14ad7326 | pastith | * CVE-2007-5461.
|
3377 | 14ad7326 | pastith | */
|
3378 | 14ad7326 | pastith | private class WebdavResolver implements EntityResolver { |
3379 | 14ad7326 | pastith | |
3380 | 14ad7326 | pastith | /**
|
3381 | 14ad7326 | pastith | * A private copy of the servlet context.
|
3382 | 14ad7326 | pastith | */
|
3383 | 14ad7326 | pastith | private ServletContext context;
|
3384 | 14ad7326 | pastith | |
3385 | 14ad7326 | pastith | /**
|
3386 | 14ad7326 | pastith | * Construct the resolver by passing the servlet context.
|
3387 | 14ad7326 | pastith | *
|
3388 | 14ad7326 | pastith | * @param theContext the servlet context
|
3389 | 14ad7326 | pastith | */
|
3390 | 14ad7326 | pastith | public WebdavResolver(ServletContext theContext) {
|
3391 | 14ad7326 | pastith | context = theContext; |
3392 | 14ad7326 | pastith | } |
3393 | 14ad7326 | pastith | |
3394 | 023f6f1e | Panagiotis Astithas | @Override
|
3395 | 14ad7326 | pastith | public InputSource resolveEntity(String publicId, String systemId) { |
3396 | 14ad7326 | pastith | context.log("The request included a reference to an external entity with PublicID " + publicId + " and SystemID " + systemId + " which was ignored"); |
3397 | 14ad7326 | pastith | return new InputSource(new StringReader("Ignored external entity")); |
3398 | 14ad7326 | pastith | } |
3399 | 14ad7326 | pastith | } |
3400 | 14ad7326 | pastith | |
3401 | 14ad7326 | pastith | /**
|
3402 | 14ad7326 | pastith | * Returns the user making the request. This is the user whose credentials
|
3403 | 14ad7326 | pastith | * were supplied in the authorization header.
|
3404 | 14ad7326 | pastith | *
|
3405 | 14ad7326 | pastith | * @param req the HTTP request
|
3406 | 14ad7326 | pastith | * @return the user making the request
|
3407 | 14ad7326 | pastith | */
|
3408 | 14ad7326 | pastith | protected User getUser(HttpServletRequest req) {
|
3409 | 14ad7326 | pastith | return (User) req.getAttribute(USER_ATTRIBUTE);
|
3410 | 14ad7326 | pastith | } |
3411 | 14ad7326 | pastith | |
3412 | 14ad7326 | pastith | /**
|
3413 | 14ad7326 | pastith | * Retrieves the user who owns the requested namespace, as specified in the
|
3414 | 14ad7326 | pastith | * REST API.
|
3415 | 14ad7326 | pastith | *
|
3416 | 14ad7326 | pastith | * @param req the HTTP request
|
3417 | 14ad7326 | pastith | * @return the owner of the namespace
|
3418 | 14ad7326 | pastith | */
|
3419 | 14ad7326 | pastith | protected User getOwner(HttpServletRequest req) {
|
3420 | 14ad7326 | pastith | return (User) req.getAttribute(OWNER_ATTRIBUTE);
|
3421 | 14ad7326 | pastith | } |
3422 | 14ad7326 | pastith | |
3423 | 62f168b2 | Giannis Koutsoubos | /**
|
3424 | 62f168b2 | Giannis Koutsoubos | * Check if the if-modified-since condition is satisfied.
|
3425 | 62f168b2 | Giannis Koutsoubos | *
|
3426 | 62f168b2 | Giannis Koutsoubos | * @param request The servlet request we are processing
|
3427 | 62f168b2 | Giannis Koutsoubos | * @param response The servlet response we are creating
|
3428 | 86c951b2 | Panagiotis Astithas | * @param folder the folder object
|
3429 | 62f168b2 | Giannis Koutsoubos | * @return boolean true if the resource meets the specified condition, and
|
3430 | 62f168b2 | Giannis Koutsoubos | * false if the condition is not satisfied, in which case request
|
3431 | 62f168b2 | Giannis Koutsoubos | * processing is stopped
|
3432 | 62f168b2 | Giannis Koutsoubos | */
|
3433 | 62f168b2 | Giannis Koutsoubos | public boolean checkIfModifiedSince(HttpServletRequest request, |
3434 | f7c44c33 | fstamatelopoulos | HttpServletResponse response, Folder folder) { |
3435 | 62f168b2 | Giannis Koutsoubos | try {
|
3436 | 62f168b2 | Giannis Koutsoubos | long headerValue = request.getDateHeader("If-Modified-Since"); |
3437 | 62f168b2 | Giannis Koutsoubos | long lastModified = folder.getAuditInfo().getModificationDate().getTime();
|
3438 | 62f168b2 | Giannis Koutsoubos | if (headerValue != -1) |
3439 | 62f168b2 | Giannis Koutsoubos | // If an If-None-Match header has been specified, if modified
|
3440 | 62f168b2 | Giannis Koutsoubos | // since is ignored.
|
3441 | 62f168b2 | Giannis Koutsoubos | if (request.getHeader("If-None-Match") == null && lastModified < headerValue + 1000) { |
3442 | 62f168b2 | Giannis Koutsoubos | // The entity has not been modified since the date
|
3443 | 62f168b2 | Giannis Koutsoubos | // specified by the client. This is not an error case.
|
3444 | 62f168b2 | Giannis Koutsoubos | response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); |
3445 | 62f168b2 | Giannis Koutsoubos | return false; |
3446 | 62f168b2 | Giannis Koutsoubos | } |
3447 | 62f168b2 | Giannis Koutsoubos | } catch (IllegalArgumentException illegalArgument) { |
3448 | 62f168b2 | Giannis Koutsoubos | return true; |
3449 | 62f168b2 | Giannis Koutsoubos | } |
3450 | 62f168b2 | Giannis Koutsoubos | return true; |
3451 | 62f168b2 | Giannis Koutsoubos | } |
3452 | 62f168b2 | Giannis Koutsoubos | |
3453 | 14ad7326 | pastith | } |