Statistics
| Branch: | Tag: | Revision:

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\">&nbsp;&nbsp;\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("&nbsp;");
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\">&nbsp;&nbsp;\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
}