Statistics
| Branch: | Tag: | Revision:

root / gss / src / gr / ebs / gss / server / rest / FilesHandler.java @ faa8d601

History | View | Annotate | Download (62.2 kB)

1
/*
2
 * Copyright 2008, 2009 Electronic Business Systems Ltd.
3
 *
4
 * This file is part of GSS.
5
 *
6
 * GSS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * GSS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
package gr.ebs.gss.server.rest;
20

    
21
import gr.ebs.gss.client.exceptions.DuplicateNameException;
22
import gr.ebs.gss.client.exceptions.GSSIOException;
23
import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
24
import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
25
import gr.ebs.gss.client.exceptions.QuotaExceededException;
26
import gr.ebs.gss.client.exceptions.RpcException;
27
import gr.ebs.gss.server.domain.FileUploadStatus;
28
import gr.ebs.gss.server.domain.User;
29
import gr.ebs.gss.server.domain.dto.FileBodyDTO;
30
import gr.ebs.gss.server.domain.dto.FileHeaderDTO;
31
import gr.ebs.gss.server.domain.dto.FolderDTO;
32
import gr.ebs.gss.server.domain.dto.GroupDTO;
33
import gr.ebs.gss.server.domain.dto.PermissionDTO;
34
import gr.ebs.gss.server.ejb.ExternalAPI;
35
import gr.ebs.gss.server.webdav.Range;
36

    
37
import java.io.BufferedReader;
38
import java.io.ByteArrayInputStream;
39
import java.io.ByteArrayOutputStream;
40
import java.io.File;
41
import java.io.FileInputStream;
42
import java.io.FileNotFoundException;
43
import java.io.IOException;
44
import java.io.InputStream;
45
import java.io.InputStreamReader;
46
import java.io.OutputStreamWriter;
47
import java.io.PrintWriter;
48
import java.io.UnsupportedEncodingException;
49
import java.net.URI;
50
import java.net.URISyntaxException;
51
import java.net.URLDecoder;
52
import java.net.URLEncoder;
53
import java.util.ArrayList;
54
import java.util.HashSet;
55
import java.util.List;
56
import java.util.Set;
57
import java.util.StringTokenizer;
58

    
59
import javax.servlet.ServletContext;
60
import javax.servlet.ServletException;
61
import javax.servlet.ServletOutputStream;
62
import javax.servlet.http.HttpServletRequest;
63
import javax.servlet.http.HttpServletResponse;
64

    
65
import org.apache.commons.fileupload.FileItemIterator;
66
import org.apache.commons.fileupload.FileItemStream;
67
import org.apache.commons.fileupload.FileUploadException;
68
import org.apache.commons.fileupload.ProgressListener;
69
import org.apache.commons.fileupload.servlet.ServletFileUpload;
70
import org.apache.commons.fileupload.util.Streams;
71
import org.apache.commons.httpclient.util.DateParseException;
72
import org.apache.commons.httpclient.util.DateUtil;
73
import org.apache.commons.logging.Log;
74
import org.apache.commons.logging.LogFactory;
75
import org.json.JSONArray;
76
import org.json.JSONException;
77
import org.json.JSONObject;
78

    
79

    
80
/**
81
 * A class that handles operations on the 'files' namespace.
82
 *
83
 * @author past
84
 */
85
public class FilesHandler extends RequestHandler {
86
        /**
87
         * The request parameter name for fetching a different version.
88
         */
89
        private static final String VERSION_PARAM = "version";
90

    
91
        /**
92
         * The request attribute containing the owner of the destination URI
93
         * in a copy or move request.
94
         */
95
        private static final String DESTINATION_OWNER_ATTRIBUTE = "destOwner";
96

    
97
        private static final int TRACK_PROGRESS_PERCENT = 5;
98

    
99
        /**
100
         * The form parameter name that contains the signature in a browser POST upload.
101
         */
102
        private static final String AUTHORIZATION_PARAMETER = "Authorization";
103

    
104
        /**
105
         * The form parameter name that contains the date in a browser POST upload.
106
         */
107
        private static final String DATE_PARAMETER = "Date";
108

    
109
        /**
110
         * The request parameter name for making an upload progress request.
111
         */
112
        private static final String PROGRESS_PARAMETER = "progress";
113

    
114
        /**
115
         * The request parameter name for restoring a previous version of a file.
116
         */
117
        private static final String RESTORE_VERSION_PARAMETER = "restoreVersion";
118

    
119
        /**
120
         * The logger.
121
         */
122
        private static Log logger = LogFactory.getLog(FilesHandler.class);
123

    
124
        /**
125
         * The servlet context provided by the call site.
126
         */
127
        private ServletContext context;
128

    
129
        /**
130
         * @param servletContext
131
         */
132
        public FilesHandler(ServletContext servletContext) {
133
                context = servletContext;
134
        }
135

    
136
        /**
137
     * Serve the specified resource, optionally including the data content.
138
     *
139
     * @param req The servlet request we are processing
140
     * @param resp The servlet response we are creating
141
     * @param content Should the content be included?
142
     *
143
     * @exception IOException if an input/output error occurs
144
     * @exception ServletException if a servlet-specified error occurs
145
     * @throws RpcException
146
     * @throws InsufficientPermissionsException
147
     * @throws ObjectNotFoundException
148
     */
149
        @Override
150
        protected void serveResource(HttpServletRequest req, HttpServletResponse resp, boolean content)
151
                    throws IOException, ServletException {
152
                boolean authDeferred = getAuthDeferred(req);
153
        String path = getInnerPath(req, PATH_FILES);
154
                if (path.equals(""))
155
                        path = "/";
156
                try {
157
                        path = URLDecoder.decode(path, "UTF-8");
158
                } catch (IllegalArgumentException e) {
159
                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
160
                        return;
161
                }
162
            String progress = req.getParameter(PROGRESS_PARAMETER);
163

    
164
            if (logger.isDebugEnabled())
165
                        if (content)
166
                            logger.debug("Serving resource '" +        path + "' headers and data");
167
                    else
168
                            logger.debug("Serving resource '" +        path + "' headers only");
169

    
170
            User user = getUser(req);
171
            User owner = getOwner(req);
172
            if (user == null) user = owner;
173
        boolean exists = true;
174
        Object resource = null;
175
        FileHeaderDTO file = null;
176
        FolderDTO folder = null;
177
        try {
178
                resource = getService().getResourceAtPath(owner.getId(), path, false);
179
        } catch (ObjectNotFoundException e) {
180
            exists = false;
181
        } catch (RpcException e) {
182
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
183
                        return;
184
                }
185

    
186
            if (!exists) {
187
                        if (authDeferred) {
188
                                // We do not want to leak information if the request
189
                                // was not authenticated.
190
                                resp.sendError(HttpServletResponse.SC_FORBIDDEN);
191
                                return;
192
                        }
193
                    // A request for upload progress.
194
                    if (progress != null && content) {
195
                            serveProgress(req, resp, progress, user, null);
196
                                return;
197
                    }
198

    
199
                    resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
200
                    return;
201
            }
202

    
203
            if (resource instanceof FolderDTO)
204
                    folder = (FolderDTO) resource;
205
            else
206
                    file = (FileHeaderDTO) resource;
207

    
208
            // Now it's time to perform the deferred authentication check.
209
                // Since regular signature checking was already performed,
210
                // we need to check the read-all flag or the signature-in-parameters.
211
                if (authDeferred)
212
                        if (file != null && !file.isReadForAll() && content) {
213
                                // Check for GET with the signature in the request parameters.
214
                                String auth = req.getParameter(AUTHORIZATION_PARAMETER);
215
                                String dateParam = req.getParameter(DATE_PARAMETER);
216
                                if (auth == null || dateParam == null) {
217
                                        resp.sendError(HttpServletResponse.SC_FORBIDDEN);
218
                                        return;
219
                                }
220

    
221
                            long timestamp;
222
                                try {
223
                                        timestamp = DateUtil.parseDate(dateParam).getTime();
224
                                } catch (DateParseException e) {
225
                                    resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
226
                                    return;
227
                                }
228
                            if (!isTimeValid(timestamp)) {
229
                                    resp.sendError(HttpServletResponse.SC_FORBIDDEN);
230
                                    return;
231
                            }
232

    
233
                                // Fetch the Authorization parameter and find the user specified in it.
234
                                String[] authParts = auth.split(" ");
235
                                if (authParts.length != 2) {
236
                                    resp.sendError(HttpServletResponse.SC_FORBIDDEN);
237
                                    return;
238
                            }
239
                                String username = authParts[0];
240
                                String signature = authParts[1];
241
                                user = null;
242
                                try {
243
                                        user = getService().findUser(username);
244
                                } catch (RpcException e) {
245
                                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
246
                                        return;
247
                                }
248
                                if (user == null) {
249
                                    resp.sendError(HttpServletResponse.SC_FORBIDDEN);
250
                                    return;
251
                            }
252
                                req.setAttribute(USER_ATTRIBUTE, user);
253

    
254
                                // Remove the servlet path from the request URI.
255
                                String p = req.getRequestURI();
256
                                String servletPath = req.getContextPath() + req.getServletPath();
257
                                p = p.substring(servletPath.length());
258
                                // Validate the signature in the Authorization parameter.
259
                                String data = req.getMethod() + dateParam + p;
260
                                if (!isSignatureValid(signature, user, data)) {
261
                                    resp.sendError(HttpServletResponse.SC_FORBIDDEN);
262
                                    return;
263
                            }
264
                        } else if (file != null && !file.isReadForAll() || file == null) {
265
                                // Check for a read-for-all file request.
266
                                resp.sendError(HttpServletResponse.SC_FORBIDDEN);
267
                                return;
268
                        }
269

    
270
            // If the resource is not a collection, and the resource path
271
            // ends with "/" or "\", return NOT FOUND.
272
            if (folder == null)
273
                        if (path.endsWith("/") || path.endsWith("\\")) {
274
                            resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
275
                            return;
276
                    }
277

    
278
            // Workaround for IE's broken caching behavior.
279
            if (folder != null)
280
                    resp.setHeader("Expires", "-1");
281

    
282
            // A request for upload progress.
283
            if (progress != null && content) {
284
                    if (file == null) {
285
                            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
286
                            return;
287
                    }
288
                    serveProgress(req, resp, progress, user, file);
289
                        return;
290
            }
291

    
292
                // Fetch the version to retrieve, if specified.
293
                String verStr = req.getParameter(VERSION_PARAM);
294
                int version = 0;
295
                FileBodyDTO oldBody = null;
296
                if (verStr != null && file != null)
297
                        try {
298
                                version = Integer.valueOf(verStr);
299
                        } catch (NumberFormatException e) {
300
                                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, req.getRequestURI());
301
                            return;
302
                        }
303
                if (version > 0)
304
                        try {
305
                                oldBody = getService().getFileVersion(user.getId(), file.getId(), version);
306
                        } catch (RpcException e) {
307
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
308
                                return;
309
                        } catch (ObjectNotFoundException e) {
310
                            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
311
                            return;
312
                        } catch (InsufficientPermissionsException e) {
313
                            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
314
                            return;
315
                        }
316

    
317
            // Check if the conditions specified in the optional If headers are
318
            // satisfied. Doing this for folders would require recursive checking
319
            // for all of their children, which in turn would defy the purpose of
320
            // the optimization.
321
            if (folder == null)
322
                        // Checking If headers.
323
                    if (!checkIfHeaders(req, resp, file, oldBody))
324
                                return;
325

    
326
            // Find content type.
327
            String contentType = null;
328
            if (file != null) {
329
                contentType = version>0 ? oldBody.getMimeType() : file.getMimeType();
330
                if (contentType == null) {
331
                        contentType = context.getMimeType(file.getName());
332
                        file.setMimeType(contentType);
333
                }
334
            } else
335
                        contentType = "application/json;charset=UTF-8";
336

    
337
            ArrayList ranges = null;
338
            long contentLength = -1L;
339

    
340
            if (file != null) {
341
                    // Parse range specifier
342
                    ranges = parseRange(req, resp, file, oldBody);
343
                    // ETag header
344
                    resp.setHeader("ETag", getETag(file, oldBody));
345
                    // Last-Modified header
346
                    String lastModified = oldBody == null ?
347
                                            getLastModifiedHttp(file.getAuditInfo()) :
348
                                            getLastModifiedHttp(oldBody.getAuditInfo());
349
                    resp.setHeader("Last-Modified", lastModified);
350
                    // X-GSS-Metadata header
351
                    try {
352
                                resp.setHeader("X-GSS-Metadata", renderJson(user, file, oldBody));
353
                        } catch (InsufficientPermissionsException e) {
354
                                resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
355
                        return;
356
                }
357
                    // Get content length
358
                    contentLength = version>0 ? oldBody.getFileSize() : file.getFileSize();
359
                    // Special case for zero length files, which would cause a
360
                    // (silent) ISE when setting the output buffer size
361
                    if (contentLength == 0L)
362
                                content = false;
363
            }
364

    
365
            ServletOutputStream ostream = null;
366
            PrintWriter writer = null;
367

    
368
            if (content)
369
                        try {
370
                            ostream = resp.getOutputStream();
371
                    } catch (IllegalStateException e) {
372
                            // If it fails, we try to get a Writer instead if we're
373
                            // trying to serve a text file
374
                            if ( contentType == null
375
                                                    || contentType.startsWith("text")
376
                                                    || contentType.endsWith("xml") )
377
                                        writer = resp.getWriter();
378
                                else
379
                                        throw e;
380
                    }
381

    
382
            if (folder != null
383
                                    || (ranges == null || ranges.isEmpty())
384
                                                            && req.getHeader("Range") == null
385
                                                            || ranges == FULL) {
386
                    // Set the appropriate output headers
387
                    if (contentType != null) {
388
                            if (logger.isDebugEnabled())
389
                                    logger.debug("contentType='" + contentType + "'");
390
                            resp.setContentType(contentType);
391
                    }
392
                    if (file != null && contentLength >= 0) {
393
                            if (logger.isDebugEnabled())
394
                                    logger.debug("contentLength=" + contentLength);
395
                            if (contentLength < Integer.MAX_VALUE)
396
                                        resp.setContentLength((int) contentLength);
397
                                else
398
                                        // Set the content-length as String to be able to use a long
399
                                    resp.setHeader("content-length", "" + contentLength);
400
                    }
401

    
402
                    InputStream renderResult = null;
403
                    if (folder != null)
404
                                if (content) {
405
                                        // Serve the directory browser
406
                                    String parentUrl = getContextPath(req, true);
407
                                    try {
408
                                                renderResult = renderJson(user, folder, parentUrl);
409
                                        } catch (InsufficientPermissionsException e) {
410
                                                resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
411
                                        return;
412
                                        }
413
                                }
414
                    // Copy the input stream to our output stream (if requested)
415
                    if (content) {
416
                            try {
417
                                    resp.setBufferSize(output);
418
                            } catch (IllegalStateException e) {
419
                                    // Silent catch
420
                            }
421
                            try {
422
                                    if(file != null)
423
                                                resp.setHeader("Content-Disposition","attachment; filename=\""+file.getName()+"\"");
424
                                    if (ostream != null)
425
                                                copy(file, renderResult, ostream, req, oldBody);
426
                                        else
427
                                                copy(file, renderResult, writer, req, oldBody);
428
                        } catch (ObjectNotFoundException e) {
429
                                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
430
                                return;
431
                        } catch (InsufficientPermissionsException e) {
432
                                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
433
                                return;
434
                        } catch (RpcException e) {
435
                                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
436
                                return;
437
                        }
438
                    }
439
            } else {
440
                    if (ranges == null || ranges.isEmpty())
441
                            return;
442
                    // Partial content response.
443
                    resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
444

    
445
                    if (ranges.size() == 1) {
446
                            Range range = (Range) ranges.get(0);
447
                            resp.addHeader("Content-Range", "bytes "
448
                                                    + range.start
449
                                                    + "-" + range.end + "/"
450
                                                    + range.length);
451
                            long length = range.end - range.start + 1;
452
                            if (length < Integer.MAX_VALUE)
453
                                        resp.setContentLength((int) length);
454
                                else
455
                                        // Set the content-length as String to be able to use a long
456
                                    resp.setHeader("content-length", "" + length);
457

    
458
                            if (contentType != null) {
459
                                    if (logger.isDebugEnabled())
460
                                            logger.debug("contentType='" + contentType + "'");
461
                                    resp.setContentType(contentType);
462
                            }
463

    
464
                            if (content) {
465
                                    try {
466
                                            resp.setBufferSize(output);
467
                                    } catch (IllegalStateException e) {
468
                                            // Silent catch
469
                                    }
470
                                    try {
471
                                            if (ostream != null)
472
                                                        copy(file, ostream, range, req, oldBody);
473
                                                else
474
                                                        copy(file, writer, range, req, oldBody);
475
                                } catch (ObjectNotFoundException e) {
476
                                        resp.sendError(HttpServletResponse.SC_NOT_FOUND);
477
                                        return;
478
                                } catch (InsufficientPermissionsException e) {
479
                                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
480
                                        return;
481
                                } catch (RpcException e) {
482
                                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
483
                                        return;
484
                                }
485
                            }
486
                    } else {
487
                            resp.setContentType("multipart/byteranges; boundary=" + mimeSeparation);
488
                            if (content) {
489
                                    try {
490
                                            resp.setBufferSize(output);
491
                                    } catch (IllegalStateException e) {
492
                                            // Silent catch
493
                                    }
494
                                    try {
495
                                            if (ostream != null)
496
                                                        copy(file, ostream, ranges.iterator(), contentType, req, oldBody);
497
                                                else
498
                                                        copy(file, writer, ranges.iterator(), contentType, req, oldBody);
499
                                } catch (ObjectNotFoundException e) {
500
                                        resp.sendError(HttpServletResponse.SC_NOT_FOUND);
501
                                        return;
502
                                } catch (InsufficientPermissionsException e) {
503
                                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
504
                                        return;
505
                                } catch (RpcException e) {
506
                                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
507
                                        return;
508
                                }
509
                            }
510
                    }
511
            }
512
    }
513

    
514
        /**
515
         * Sends a progress update on the amount of bytes received until now for
516
         * a file that the current user is currently uploading.
517
         *
518
         * @param req the HTTP request
519
         * @param resp the HTTP response
520
         * @param parameter the value for the progress request parameter
521
         * @param user the current user
522
         * @param file the file being uploaded, or null if the request is about a new file
523
         * @throws IOException if an I/O error occurs
524
         */
525
        private void serveProgress(HttpServletRequest req, HttpServletResponse resp,
526
                                String parameter, User user, FileHeaderDTO file)        throws IOException {
527
                String filename = file == null ? parameter : file.getName();
528
                try {
529
                        FileUploadStatus status = getService().getFileUploadStatus(user.getId(), filename);
530
                        if (status == null) {
531
                                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
532
                                return;
533
                        }
534
                        JSONObject json = new JSONObject();
535
                        json.put("bytesUploaded", status.getBytesUploaded()).
536
                                put("bytesTotal", status.getFileSize());
537
                        sendJson(req, resp, json.toString());
538

    
539
                        // Workaround for IE's broken caching behavior.
540
                    resp.setHeader("Expires", "-1");
541
                        return;
542
                } catch (RpcException e) {
543
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
544
                        return;
545
                } catch (JSONException e) {
546
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
547
                        return;
548
                }
549
        }
550

    
551
        /**
552
         * Server a POST request to create/modify a file or folder.
553
         *
554
         * @param req the HTTP request
555
         * @param resp the HTTP response
556
     * @exception IOException if an input/output error occurs
557
         */
558
        void postResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
559
                boolean authDeferred = getAuthDeferred(req);
560
            if (!authDeferred && req.getParameterMap().size() > 1) {
561
                    resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
562
                    return;
563
            }
564
        String path = getInnerPath(req, PATH_FILES);
565
            path = path.endsWith("/")? path: path + '/';
566
            path = URLDecoder.decode(path, "UTF-8");
567
            // We only defer authenticating multipart POST requests.
568
            if (authDeferred) {
569
                        if (!ServletFileUpload.isMultipartContent(req)) {
570
                            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
571
                            return;
572
                    }
573
                        handleMultipart(req, resp, path);
574
                        return;
575
                }
576

    
577
            String newName = req.getParameter(NEW_FOLDER_PARAMETER);
578
            boolean hasUpdateParam = req.getParameterMap().containsKey(RESOURCE_UPDATE_PARAMETER);
579
            boolean hasTrashParam = req.getParameterMap().containsKey(RESOURCE_TRASH_PARAMETER);
580
            boolean hasRestoreParam = req.getParameterMap().containsKey(RESOURCE_RESTORE_PARAMETER);
581
            String copyTo = req.getParameter(RESOURCE_COPY_PARAMETER);
582
            String moveTo = req.getParameter(RESOURCE_MOVE_PARAMETER);
583
            String restoreVersion = req.getParameter(RESTORE_VERSION_PARAMETER);
584

    
585
            if (newName != null) {
586
                    try {
587
                            newName = URLDecoder.decode(newName, "UTF-8");
588
                    } catch (IllegalArgumentException e) {
589
                            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
590
                            return;
591
                    }
592
                        createFolder(req, resp, path, newName);
593
            } else if (hasUpdateParam)
594
                        updateResource(req, resp, path);
595
                else if (hasTrashParam)
596
                        trashResource(req, resp, path);
597
                else if (hasRestoreParam)
598
                        restoreResource(req, resp, path);
599
                else if (copyTo != null)
600
                        copyResource(req, resp, path, copyTo);
601
                else if (moveTo != null)
602
                        moveResource(req, resp, path, moveTo);
603
                else if (restoreVersion != null)
604
                        restoreVersion(req, resp, path, restoreVersion);
605
                else
606
                        resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
607
        }
608

    
609
        /**
610
         * Restores a previous version for a file.
611
         *
612
         * @param req the HTTP request
613
         * @param resp the HTTP response
614
         * @param path the resource path
615
         * @param version the version number to restore
616
         * @throws IOException if an I/O error occurs
617
         */
618
        private void restoreVersion(HttpServletRequest req, HttpServletResponse resp, String path, String version) throws IOException {
619
                User user = getUser(req);
620
                User owner = getOwner(req);
621
                Object resource = null;
622
                try {
623
                        resource = getService().getResourceAtPath(owner.getId(), path, true);
624
                } catch (ObjectNotFoundException e) {
625
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
626
                        return;
627
                } catch (RpcException e) {
628
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
629
                        return;
630
                }
631
                if (resource instanceof FolderDTO) {
632
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
633
                        return;
634
                }
635

    
636
                try {
637
                        FileHeaderDTO file = (FileHeaderDTO) resource;
638
                        int oldVersion = Integer.parseInt(version);
639
                        getService().restoreVersion(user.getId(), file.getId(), oldVersion);
640
                } catch (InsufficientPermissionsException e) {
641
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
642
                } catch (ObjectNotFoundException e) {
643
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
644
                } catch (RpcException e) {
645
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
646
                } catch (GSSIOException e) {
647
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
648
                } catch (QuotaExceededException e) {
649
                        resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
650
                } catch (NumberFormatException e) {
651
                        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
652
                }
653
        }
654

    
655
        /**
656
         * A method for handling multipart POST requests for uploading
657
         * files from browser-based JavaScript clients.
658
         *
659
         * @param request the HTTP request
660
         * @param response the HTTP response
661
         * @param path the resource path
662
         * @throws IOException in case an error occurs writing to the
663
         *                 response stream
664
         */
665
        private void handleMultipart(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
666
            if (logger.isDebugEnabled())
667
                           logger.debug("Multipart POST for resource: " + path);
668

    
669
            User owner = getOwner(request);
670
            boolean exists = true;
671
        Object resource = null;
672
        FileHeaderDTO file = null;
673
        try {
674
                resource = getService().getResourceAtPath(owner.getId(), path, false);
675
        } catch (ObjectNotFoundException e) {
676
            exists = false;
677
        } catch (RpcException e) {
678
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
679
                        return;
680
                }
681

    
682
        if (exists)
683
                        if (resource instanceof FileHeaderDTO) {
684
                            file = (FileHeaderDTO) resource;
685
                            if (file.isDeleted()) {
686
                                    response.sendError(HttpServletResponse.SC_CONFLICT, file.getName() + " is in the trash");
687
                                return;
688
                            }
689
                        } else {
690
                        response.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
691
                            return;
692
                }
693

    
694
        FolderDTO folder = null;
695
            Object parent;
696
            String parentPath = null;
697
                try {
698
                        parentPath = getParentPath(path);
699
                        parent = getService().getResourceAtPath(owner.getId(), parentPath, true);
700
                } catch (ObjectNotFoundException e) {
701
                    response.sendError(HttpServletResponse.SC_NOT_FOUND, parentPath);
702
                    return;
703
                } catch (RpcException e) {
704
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
705
                        return;
706
                }
707
            if (!(parent instanceof FolderDTO)) {
708
                    response.sendError(HttpServletResponse.SC_CONFLICT);
709
                    return;
710
            }
711
                   folder = (FolderDTO) parent;
712
            String fileName = getLastElement(path);
713

    
714
                FileItemIterator iter;
715
                File uploadedFile = null;
716
                try {
717
                        // Create a new file upload handler.
718
                        ServletFileUpload upload = new ServletFileUpload();
719
                        StatusProgressListener progressListener = new StatusProgressListener(getService());
720
                        upload.setProgressListener(progressListener);
721
                        iter = upload.getItemIterator(request);
722
                        String dateParam = null;
723
                        String auth = null;
724
                        while (iter.hasNext()) {
725
                                FileItemStream item = iter.next();
726
                                String name = item.getFieldName();
727
                                InputStream stream = item.openStream();
728
                                if (item.isFormField()) {
729
                                        final String value = Streams.asString(stream);
730
                                        if (name.equals(DATE_PARAMETER))
731
                                                dateParam = value;
732
                                        else if (name.equals(AUTHORIZATION_PARAMETER))
733
                                                auth = value;
734

    
735
                                        if (logger.isDebugEnabled())
736
                                                logger.debug(name + ":" + value);
737
                                } else {
738
                                        // Fetch the timestamp used to guard against replay attacks.
739
                                    if (dateParam == null) {
740
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Date parameter");
741
                                            return;
742
                                    }
743

    
744
                                    long timestamp;
745
                                        try {
746
                                                timestamp = DateUtil.parseDate(dateParam).getTime();
747
                                        } catch (DateParseException e) {
748
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
749
                                            return;
750
                                        }
751
                                    if (!isTimeValid(timestamp)) {
752
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN);
753
                                            return;
754
                                    }
755

    
756
                                        // Fetch the Authorization parameter and find the user specified in it.
757
                                    if (auth == null) {
758
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Authorization parameter");
759
                                            return;
760
                                    }
761
                                        String[] authParts = auth.split(" ");
762
                                        if (authParts.length != 2) {
763
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN);
764
                                            return;
765
                                    }
766
                                        String username = authParts[0];
767
                                        String signature = authParts[1];
768
                                        User user = null;
769
                                        try {
770
                                                user = getService().findUser(username);
771
                                        } catch (RpcException e) {
772
                                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
773
                                                return;
774
                                        }
775
                                        if (user == null) {
776
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN);
777
                                            return;
778
                                    }
779
                                        request.setAttribute(USER_ATTRIBUTE, user);
780

    
781
                                        // Remove the servlet path from the request URI.
782
                                        String p = request.getRequestURI();
783
                                        String servletPath = request.getContextPath() + request.getServletPath();
784
                                        p = p.substring(servletPath.length());
785
                                        // Validate the signature in the Authorization parameter.
786
                                        String data = request.getMethod() + dateParam + p;
787
                                        if (!isSignatureValid(signature, user, data)) {
788
                                            response.sendError(HttpServletResponse.SC_FORBIDDEN);
789
                                            return;
790
                                    }
791

    
792
                                        progressListener.setUserId(user.getId());
793
                                        progressListener.setFilename(fileName);
794
                                        String contentType = item.getContentType();
795

    
796
                                        try {
797
                                                uploadedFile = getService().uploadFile(stream, user.getId());
798
                                        } catch (IOException ex) {
799
                                                throw new GSSIOException(ex, false);
800
                                        }
801
                                        if (file == null)
802
                                                getService().createFile(user.getId(), folder.getId(), fileName, contentType, uploadedFile);
803
                                        else
804
                                                getService().updateFileContents(user.getId(), file.getId(), contentType, uploadedFile);
805
                                        getService().removeFileUploadProgress(user.getId(), fileName);
806
                                }
807
                        }
808
                        // We can't return 204 here since GWT's onSubmitComplete won't fire.
809
                        response.setContentType("text/html");
810
            response.getWriter().print("<pre></pre>");
811
                } catch (FileUploadException e) {
812
                        String error = "Error while uploading file";
813
                        logger.error(error, e);
814
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
815
                } catch (GSSIOException e) {
816
                        if (uploadedFile != null && uploadedFile.exists())
817
                                uploadedFile.delete();
818
                        String error = "Error while uploading file";
819
                        if (e.logAsError())
820
                                logger.error(error, e);
821
                        else
822
                                logger.debug(error, e);
823
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
824
                } catch (DuplicateNameException e) {
825
                        if (uploadedFile != null && uploadedFile.exists())
826
                                uploadedFile.delete();
827
                        String error = "The specified file name already exists in this folder";
828
                        logger.error(error, e);
829
                        response.sendError(HttpServletResponse.SC_CONFLICT, error);
830

    
831
                } catch (InsufficientPermissionsException e) {
832
                        if (uploadedFile != null && uploadedFile.exists())
833
                                uploadedFile.delete();
834
                        String error = "You don't have the necessary permissions";
835
                        logger.error(error, e);
836
                        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, error);
837

    
838
                } catch (QuotaExceededException e) {
839
                        if (uploadedFile != null && uploadedFile.exists())
840
                                uploadedFile.delete();
841
                        String error = "Not enough free space available";
842
                        if (logger.isDebugEnabled())
843
                                logger.debug(error, e);
844
                        response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, error);
845

    
846
                } catch (ObjectNotFoundException e) {
847
                        if (uploadedFile != null && uploadedFile.exists())
848
                                uploadedFile.delete();
849
                        String error = "A specified object was not found";
850
                        logger.error(error, e);
851
                        response.sendError(HttpServletResponse.SC_NOT_FOUND, error);
852
                } catch (RpcException e) {
853
                        if (uploadedFile != null && uploadedFile.exists())
854
                                uploadedFile.delete();
855
                        String error = "An error occurred while communicating with the service";
856
                        logger.error(error, e);
857
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
858
                }
859
        }
860

    
861
        private String getBackupFilename(FileHeaderDTO file, String filename){
862
                List<FileHeaderDTO> deletedFiles = new ArrayList<FileHeaderDTO>();
863
                try{
864
                        deletedFiles = getService().getDeletedFiles(file.getOwner().getId());
865
                }
866
                catch(ObjectNotFoundException e){
867

    
868
                } catch (RpcException e) {
869

    
870
                }
871
                List<FileHeaderDTO> filesInSameFolder = new ArrayList<FileHeaderDTO>();
872
                for(FileHeaderDTO deleted : deletedFiles)
873
                        if(deleted.getFolder().getId().equals(file.getFolder().getId()))
874
                                filesInSameFolder.add(deleted);
875
                int i=1;
876
                String filenameToCheck = filename;
877
                for(FileHeaderDTO same : filesInSameFolder)
878
                        if(same.getName().startsWith(filename)){
879
                                String toCheck=same.getName().substring(filename.length(),same.getName().length());
880
                                if(toCheck.startsWith(" ")){
881
                                        int test =-1;
882
                                        try{
883
                                                test = Integer.valueOf(toCheck.replace(" ",""));
884
                                        }
885
                                        catch(NumberFormatException e){
886
                                                //do nothing since string is not a number
887
                                        }
888
                                        if(test>=i)
889
                                                i = test+1;
890
                                }
891
                        }
892

    
893
                return filename+" "+i;
894
        }
895

    
896
        /**
897
         * Move the resource in the specified path to the specified destination.
898
         *
899
         * @param req the HTTP request
900
         * @param resp the HTTP response
901
         * @param path the path of the resource
902
         * @param moveTo the destination of the move procedure
903
         * @throws IOException if an input/output error occurs
904
         */
905
        private void moveResource(HttpServletRequest req, HttpServletResponse resp, String path, String moveTo) throws IOException {
906
                User user = getUser(req);
907
                User owner = getOwner(req);
908
                Object resource = null;
909
                try {
910
                        resource = getService().getResourceAtPath(owner.getId(), path, true);
911
                } catch (ObjectNotFoundException e) {
912
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
913
                        return;
914
                } catch (RpcException e) {
915
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
916
                        return;
917
                }
918

    
919
        String destination = null;
920
        User destOwner = null;
921
                boolean exists = true;
922
                try {
923
                        destination = getDestinationPath(req, encodePath(moveTo));
924
                        destination = URLDecoder.decode(destination, "UTF-8");
925
                        destOwner = getDestinationOwner(req);
926
                        getService().getResourceAtPath(destOwner.getId(), destination, true);
927
                } catch (ObjectNotFoundException e) {
928
                        exists = false;
929
                } catch (URISyntaxException e) {
930
                        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
931
                        return;
932
                } catch (RpcException e) {
933
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
934
                        return;
935
                }
936
                if (exists) {
937
                        resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
938
                        return;
939
                }
940

    
941
                try {
942
                        if (resource instanceof FolderDTO) {
943
                                FolderDTO folder = (FolderDTO) resource;
944
                                getService().moveFolderToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
945
                        } else {
946
                                FileHeaderDTO file = (FileHeaderDTO) resource;
947
                                getService().moveFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
948
                        }
949
                } catch (InsufficientPermissionsException e) {
950
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
951
                } catch (ObjectNotFoundException e) {
952
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
953
                } catch (RpcException e) {
954
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
955
                } catch (DuplicateNameException e) {
956
                        resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
957
                } catch (GSSIOException e) {
958
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
959
                } catch (QuotaExceededException e) {
960
                        resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
961
                }
962
        }
963

    
964
        /**
965
         * Copy the resource in the specified path to the specified destination.
966
         *
967
         * @param req the HTTP request
968
         * @param resp the HTTP response
969
         * @param path the path of the resource
970
         * @param copyTo the destination of the copy procedure
971
         * @throws IOException if an input/output error occurs
972
         */
973
        private void copyResource(HttpServletRequest req, HttpServletResponse resp, String path, String copyTo) throws IOException {
974
                User user = getUser(req);
975
                User owner = getOwner(req);
976
                Object resource = null;
977
                try {
978
                        resource = getService().getResourceAtPath(owner.getId(), path, true);
979
                } catch (ObjectNotFoundException e) {
980
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
981
                        return;
982
                } catch (RpcException e) {
983
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
984
                        return;
985
                }
986

    
987
        String destination = null;
988
        User destOwner = null;
989
                boolean exists = true;
990
                try {
991
                        destination = getDestinationPath(req, encodePath(copyTo));
992
                        destination = URLDecoder.decode(destination, "UTF-8");
993
                        destOwner = getDestinationOwner(req);
994
                        getService().getResourceAtPath(destOwner.getId(), destination, true);
995
                } catch (ObjectNotFoundException e) {
996
                        exists = false;
997
                } catch (URISyntaxException e) {
998
                        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
999
                        return;
1000
                } catch (RpcException e) {
1001
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1002
                        return;
1003
                }
1004
                if (exists) {
1005
                        resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
1006
                        return;
1007
                }
1008

    
1009
                try {
1010
                        if (resource instanceof FolderDTO) {
1011
                                FolderDTO folder = (FolderDTO) resource;
1012
                                getService().copyFolderStructureToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
1013
                        } else {
1014
                                FileHeaderDTO file = (FileHeaderDTO) resource;
1015
                                getService().copyFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
1016
                        }
1017
                } catch (InsufficientPermissionsException e) {
1018
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1019
                } catch (ObjectNotFoundException e) {
1020
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1021
                } catch (RpcException e) {
1022
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
1023
                } catch (DuplicateNameException e) {
1024
                        resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
1025
                } catch (GSSIOException e) {
1026
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
1027
                } catch (QuotaExceededException e) {
1028
                        resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1029
                }
1030
        }
1031

    
1032
        private String encodePath(String path) throws UnsupportedEncodingException{
1033
                StringTokenizer str = new StringTokenizer(path, "/:", true);
1034
                String result = new String();
1035
                while(str.hasMoreTokens()){
1036
                        String token = str.nextToken();
1037
                        if(!token.equals("/") && !token.equals(":"))
1038
                                token = URLEncoder.encode(token,"UTF-8");
1039
                        result = result + token;
1040
                }
1041
                return result;
1042
        }
1043
        /**
1044
         * A helper method that extracts the relative resource path,
1045
         * after removing the 'files' namespace.
1046
         *
1047
         * @param req the HTTP request
1048
         * @param path the specified path
1049
         * @return the path relative to the root folder
1050
         * @throws URISyntaxException
1051
         * @throws RpcException in case an error occurs while communicating
1052
         *                                                 with the backend
1053
         */
1054
        private String getDestinationPath(HttpServletRequest req, String path) throws URISyntaxException, RpcException {
1055
                URI uri = new URI(path);
1056
                String dest = uri.getPath();
1057
                // Remove the context path from the destination URI.
1058
                String contextPath = req.getContextPath();
1059
                if (!dest.startsWith(contextPath))
1060
                        throw new URISyntaxException(dest, "Destination path does not start with " + contextPath);
1061
                dest = dest.substring(contextPath.length());
1062
                // Remove the servlet path from the destination URI.
1063
                String servletPath = req.getServletPath();
1064
                if (!dest.startsWith(servletPath))
1065
                        throw new URISyntaxException(dest, "Destination path does not start with " + servletPath);
1066
                dest = dest.substring(servletPath.length());
1067
            // Strip the username part
1068
                if (dest.length() < 2)
1069
                        throw new URISyntaxException(dest, "No username in the destination URI");
1070
                int slash = dest.substring(1).indexOf('/');
1071
                if (slash == -1)
1072
                        throw new URISyntaxException(dest, "No username in the destination URI");
1073
                String owner = dest.substring(1, slash + 1);
1074
                User o;
1075
                o = getService().findUser(owner);
1076
                if (o == null)
1077
                        throw new URISyntaxException(dest, "User " + owner + " not found");
1078

    
1079
                req.setAttribute(DESTINATION_OWNER_ATTRIBUTE, o);
1080
                dest = dest.substring(slash + 1);
1081

    
1082
                // Chop the resource namespace part
1083
                dest = dest.substring(RequestHandler.PATH_FILES.length());
1084

    
1085
            dest = dest.endsWith("/")? dest: dest + '/';
1086
                return dest;
1087
        }
1088

    
1089
        /**
1090
         * Move the resource in the specified path to the trash bin.
1091
         *
1092
         * @param req the HTTP request
1093
         * @param resp the HTTP response
1094
         * @param path the path of the resource
1095
         * @throws IOException if an input/output error occurs
1096
         */
1097
        private void trashResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1098
                User user = getUser(req);
1099
                User owner = getOwner(req);
1100
                Object resource = null;
1101
                try {
1102
                        resource = getService().getResourceAtPath(owner.getId(), path, true);
1103
                } catch (ObjectNotFoundException e) {
1104
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1105
                        return;
1106
                } catch (RpcException e) {
1107
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1108
                        return;
1109
                }
1110

    
1111
                try {
1112
                        if (resource instanceof FolderDTO) {
1113
                                FolderDTO folder = (FolderDTO) resource;
1114
                                getService().moveFolderToTrash(user.getId(), folder.getId());
1115
                        } else {
1116
                                FileHeaderDTO file = (FileHeaderDTO) resource;
1117
                                getService().moveFileToTrash(user.getId(), file.getId());
1118
                        }
1119
                } catch (InsufficientPermissionsException e) {
1120
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1121
                } catch (ObjectNotFoundException e) {
1122
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1123
                } catch (RpcException e) {
1124
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1125
                }
1126
        }
1127

    
1128
        /**
1129
         * Restore the resource in the specified path from the trash bin.
1130
         *
1131
         * @param req the HTTP request
1132
         * @param resp the HTTP response
1133
         * @param path the path of the resource
1134
         * @throws IOException if an input/output error occurs
1135
         */
1136
        private void restoreResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1137
                User user = getUser(req);
1138
                User owner = getOwner(req);
1139
                Object resource = null;
1140
                try {
1141
                        resource = getService().getResourceAtPath(owner.getId(), path, false);
1142
                } catch (ObjectNotFoundException e) {
1143
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1144
                        return;
1145
                } catch (RpcException e) {
1146
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1147
                        return;
1148
                }
1149

    
1150
                try {
1151
                        if (resource instanceof FolderDTO) {
1152
                                FolderDTO folder = (FolderDTO) resource;
1153
                                getService().removeFolderFromTrash(user.getId(), folder.getId());
1154
                        } else {
1155
                                FileHeaderDTO file = (FileHeaderDTO) resource;
1156
                                getService().removeFileFromTrash(user.getId(), file.getId());
1157
                        }
1158
                } catch (InsufficientPermissionsException e) {
1159
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1160
                } catch (ObjectNotFoundException e) {
1161
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1162
                } catch (RpcException e) {
1163
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1164
                }
1165
        }
1166

    
1167
        /**
1168
         * Update the resource in the specified path.
1169
         *
1170
         * @param req the HTTP request
1171
         * @param resp the HTTP response
1172
         * @param path the path of the resource
1173
         * @throws IOException if an input/output error occurs
1174
         */
1175
        private void updateResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1176
                User user = getUser(req);
1177
                User owner = getOwner(req);
1178
                Object resource = null;
1179
                try {
1180
                        resource = getService().getResourceAtPath(owner.getId(), path, false);
1181
                } catch (ObjectNotFoundException e) {
1182
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1183
                        return;
1184
                } catch (RpcException e) {
1185
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1186
                        return;
1187
                }
1188
                //use utf-8 encoding for reading request
1189
                BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8"));
1190
                StringBuffer input = new StringBuffer();
1191
                String line = null;
1192
                JSONObject json = null;
1193
                while ((line = reader.readLine()) != null)
1194
                        input.append(line);
1195
                reader.close();
1196
                try {
1197
                        json = new JSONObject(input.toString());
1198
                        if (logger.isDebugEnabled())
1199
                                logger.debug("JSON update: " + json);
1200
                        if (resource instanceof FolderDTO) {
1201
                                FolderDTO folder = (FolderDTO) resource;
1202
                                String name = json.optString("name");
1203
                                if (!name.isEmpty()){
1204
                                        try {
1205
                                                name = URLDecoder.decode(name, "UTF-8");
1206
                                        } catch (IllegalArgumentException e) {
1207
                                                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1208
                                                return;
1209
                                        }
1210
                                        getService().modifyFolder(user.getId(), folder.getId(), name);
1211
                                        FolderDTO folderUpdated = getService().getFolder(user.getId(), folder.getId());
1212
                                        String parentUrl =URLDecoder.decode(getContextPath(req, true),"UTF-8");
1213
                                        String fpath = URLDecoder.decode(req.getPathInfo(), "UTF-8");
1214
                                        parentUrl = parentUrl.replaceAll(fpath, "");
1215
                                        if(!parentUrl.endsWith("/"))
1216
                                                parentUrl = parentUrl+"/";
1217
                                        parentUrl = parentUrl+folderUpdated.getOwner().getUsername()+PATH_FILES+folderUpdated.getPath();
1218
                                        resp.getWriter().println(parentUrl);
1219
                                }
1220

    
1221
                                JSONArray permissions = json.optJSONArray("permissions");
1222
                                if (permissions != null) {
1223
                                        Set<PermissionDTO> perms = parsePermissions(user, permissions);
1224
                                        getService().setFolderPermissions(user.getId(), folder.getId(), perms);
1225
                                }
1226
                        } else {
1227
                                FileHeaderDTO file = (FileHeaderDTO) resource;
1228
                                String name = null;
1229
                                if (json.opt("name") != null)
1230
                                        name = json.optString("name");
1231
                                JSONArray tagset = json.optJSONArray("tags");
1232
                                String tags = null;
1233
                                StringBuffer t = new StringBuffer();
1234
                                if (tagset != null) {
1235
                                        for (int i = 0; i < tagset.length(); i++)
1236
                                                t.append(tagset.getString(i) + ',');
1237
                                        tags = t.toString();
1238
                                }
1239
                                if (name != null || tags != null)
1240
                                        getService().updateFile(user.getId(), file.getId(), name, tags);
1241

    
1242
                                JSONArray permissions = json.optJSONArray("permissions");
1243
                                Set<PermissionDTO> perms = null;
1244
                                if (permissions != null)
1245
                                        perms = parsePermissions(user, permissions);
1246
                                Boolean readForAll = null;
1247
                                if (json.opt("readForAll") != null)
1248
                                        readForAll = json.optBoolean("readForAll");
1249
                                if (perms != null || readForAll != null)
1250
                                        getService().setFilePermissions(user.getId(), file.getId(), readForAll, perms);
1251

    
1252
                                if (json.opt("versioned") != null) {
1253
                                        boolean versioned = json.getBoolean("versioned");
1254
                                        getService().toggleFileVersioning(user.getId(), file.getId(), versioned);
1255
                                }
1256
                        }
1257
                } catch (JSONException e) {
1258
                        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1259
                } catch (InsufficientPermissionsException e) {
1260
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1261
                } catch (ObjectNotFoundException e) {
1262
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1263
                } catch (DuplicateNameException e) {
1264
                        resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
1265
                } catch (RpcException e) {
1266
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1267
                }
1268
        }
1269

    
1270
        /**
1271
         * Helper method to convert a JSON array of permissions into a set of
1272
         * PermissionDTO objects.
1273
         *
1274
         * @param user the current user
1275
         * @param permissions the JSON array to parse
1276
         * @return the parsed set of permissions
1277
         * @throws JSONException if there was an error parsing the JSON object
1278
         * @throws RpcException if there was an error communicating with the EJB
1279
         * @throws ObjectNotFoundException if the user could not be found
1280
         */
1281
        private Set<PermissionDTO> parsePermissions(User user, JSONArray permissions)
1282
                        throws JSONException, RpcException, ObjectNotFoundException {
1283
                if (permissions == null)
1284
                        return null;
1285
                Set<PermissionDTO> perms = new HashSet<PermissionDTO>();
1286
                for (int i = 0; i < permissions.length(); i++) {
1287
                        JSONObject j = permissions.getJSONObject(i);
1288
                        PermissionDTO perm = new PermissionDTO();
1289
                        perm.setModifyACL(j.optBoolean("modifyACL"));
1290
                        perm.setRead(j.optBoolean("read"));
1291
                        perm.setWrite(j.optBoolean("write"));
1292
                        String permUser = j.optString("user");
1293
                        if (!permUser.isEmpty()) {
1294
                                User u = getService().findUser(permUser);
1295
                                if (u == null)
1296
                                        throw new ObjectNotFoundException("User " + permUser + " not found");
1297
                                perm.setUser(u.getDTO());
1298
                        }
1299
                        String permGroup = j.optString("group");
1300
                        if (!permGroup.isEmpty()) {
1301
                                GroupDTO g = getService().getGroup(user.getId(), permGroup);
1302
                                perm.setGroup(g);
1303
                        }
1304
                        if (permUser.isEmpty() && permGroup.isEmpty() ||
1305
                                                permUser.isEmpty() && permGroup.isEmpty())
1306
                                throw new JSONException("A permission must correspond to either a user or a group");
1307
                        perms.add(perm);
1308
                }
1309
                return perms;
1310
        }
1311

    
1312
        /**
1313
         * Creates a new folder with the specified name under the folder in the provided path.
1314
         *
1315
         * @param req the HTTP request
1316
         * @param resp the HTTP response
1317
         * @param path the parent folder path
1318
         * @param folderName the name of the new folder
1319
         * @throws IOException if an input/output error occurs
1320
         */
1321
        private void createFolder(HttpServletRequest req, HttpServletResponse resp, String path, String folderName) throws IOException {
1322
                if (logger.isDebugEnabled())
1323
                           logger.debug("Creating folder " + folderName + " in '" + path);
1324

    
1325
            User user = getUser(req);
1326
            User owner = getOwner(req);
1327
        boolean exists = true;
1328
        try {
1329
                getService().getResourceAtPath(owner.getId(), path + folderName, false);
1330
        } catch (ObjectNotFoundException e) {
1331
            exists = false;
1332
        } catch (RpcException e) {
1333
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1334
                        return;
1335
                }
1336

    
1337
        if (exists) {
1338
            resp.addHeader("Allow", METHOD_GET + ", " + METHOD_DELETE +
1339
                                    ", " + METHOD_HEAD);
1340
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1341
            return;
1342
        }
1343

    
1344
                Object parent;
1345
                try {
1346
                        parent = getService().getResourceAtPath(owner.getId(), path, true);
1347
                } catch (ObjectNotFoundException e) {
1348
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
1349
                        return;
1350
                } catch (RpcException e) {
1351
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1352
                        return;
1353
                }
1354
                try {
1355
                        if (parent instanceof FolderDTO) {
1356
                                FolderDTO folder = (FolderDTO) parent;
1357
                                getService().createFolder(user.getId(), folder.getId(), folderName);
1358
                        String newResource = getContextPath(req, true) + folderName;
1359
                        resp.setHeader("Location", newResource);
1360
                        resp.setContentType("text/plain");
1361
                        PrintWriter out = resp.getWriter();
1362
                        out.println(newResource);
1363
                        } else {
1364
                                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1365
                            return;
1366
                        }
1367
                } catch (DuplicateNameException e) {
1368
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
1369
                    return;
1370
                } catch (InsufficientPermissionsException e) {
1371
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1372
                    return;
1373
                } catch (ObjectNotFoundException e) {
1374
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
1375
                        return;
1376
                } catch (RpcException e) {
1377
                        resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1378
                        return;
1379
                }
1380
            resp.setStatus(HttpServletResponse.SC_CREATED);
1381
        }
1382

    
1383
        /**
1384
         * @param req
1385
         * @param resp
1386
         * @throws IOException
1387
         * @throws FileNotFoundException
1388
         */
1389
        void putResource(HttpServletRequest req, HttpServletResponse resp) throws IOException, FileNotFoundException {
1390
        String path = getInnerPath(req, PATH_FILES);
1391
            if (logger.isDebugEnabled())
1392
                           logger.debug("Updating resource: " + path);
1393

    
1394
            User user = getUser(req);
1395
            User owner = getOwner(req);
1396
            boolean exists = true;
1397
        Object resource = null;
1398
        FileHeaderDTO file = null;
1399
        try {
1400
                resource = getService().getResourceAtPath(owner.getId(), path, false);
1401
        } catch (ObjectNotFoundException e) {
1402
            exists = false;
1403
        } catch (RpcException e) {
1404
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1405
                        return;
1406
                }
1407

    
1408
        if (exists)
1409
                        if (resource instanceof FileHeaderDTO)
1410
                            file = (FileHeaderDTO) resource;
1411
                        else {
1412
                        resp.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
1413
                            return;
1414
                }
1415
        boolean result = true;
1416

    
1417
        // Temporary content file used to support partial PUT.
1418
        File contentFile = null;
1419

    
1420
        Range range = parseContentRange(req, resp);
1421

    
1422
        InputStream resourceInputStream = null;
1423

    
1424
        // Append data specified in ranges to existing content for this
1425
        // resource - create a temporary file on the local filesystem to
1426
        // perform this operation.
1427
        // Assume just one range is specified for now
1428
        if (range != null) {
1429
            try {
1430
                                contentFile = executePartialPut(req, range, path);
1431
                        } catch (RpcException e) {
1432
                                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1433
                                return;
1434
                        } catch (ObjectNotFoundException e) {
1435
                                resp.sendError(HttpServletResponse.SC_CONFLICT);
1436
                        return;
1437
                        } catch (InsufficientPermissionsException e) {
1438
                                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1439
                        return;
1440
                        }
1441
            resourceInputStream = new FileInputStream(contentFile);
1442
        } else
1443
                        resourceInputStream = req.getInputStream();
1444

    
1445
        try {
1446
                FolderDTO folder = null;
1447
                Object parent = getService().getResourceAtPath(owner.getId(), getParentPath(path), true);
1448
                if (!(parent instanceof FolderDTO)) {
1449
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
1450
                        return;
1451
                }
1452
                       folder = (FolderDTO) parent;
1453
                String name = getLastElement(path);
1454
                String mimeType = context.getMimeType(name);
1455
            // FIXME: Add attributes
1456
            if (exists)
1457
                                getService().updateFileContents(user.getId(), file.getId(), mimeType, resourceInputStream);
1458
                        else
1459
                        getService().createFile(user.getId(), folder.getId(), name, mimeType, resourceInputStream);
1460
                        getService().removeFileUploadProgress(user.getId(), file.getName());
1461
        } catch(ObjectNotFoundException e) {
1462
            result = false;
1463
        } catch (RpcException e) {
1464
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1465
                        return;
1466
        } catch (IOException e) {
1467
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1468
                        return;
1469
                } catch (GSSIOException e) {
1470
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1471
                        return;
1472
                } catch (DuplicateNameException e) {
1473
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
1474
                    return;
1475
                } catch (InsufficientPermissionsException e) {
1476
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1477
                    return;
1478
                } catch (QuotaExceededException e) {
1479
                        resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1480
                    return;
1481
                }
1482

    
1483
        if (result) {
1484
            if (exists)
1485
                                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1486
                        else
1487
                                resp.setStatus(HttpServletResponse.SC_CREATED);
1488
        } else
1489
                        resp.sendError(HttpServletResponse.SC_CONFLICT);
1490
        }
1491

    
1492
    /**
1493
     * Delete a resource.
1494
     *
1495
     * @param req The servlet request we are processing
1496
     * @param resp The servlet response we are processing
1497
         * @throws IOException if the response cannot be sent
1498
     */
1499
    void deleteResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
1500
        String path = getInnerPath(req, PATH_FILES);
1501
            if (logger.isDebugEnabled())
1502
                           logger.debug("Deleting resource '" + path);
1503
            path = URLDecoder.decode(path, "UTF-8");
1504
            User user = getUser(req);
1505
            User owner = getOwner(req);
1506
            boolean exists = true;
1507
            Object object = null;
1508
            try {
1509
                    object = getService().getResourceAtPath(owner.getId(), path, false);
1510
            } catch (ObjectNotFoundException e) {
1511
                    exists = false;
1512
            } catch (RpcException e) {
1513
                    resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1514
                        return;
1515
                }
1516

    
1517
            if (!exists) {
1518
                    resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1519
                    return;
1520
            }
1521

    
1522
            FolderDTO folder = null;
1523
            FileHeaderDTO file = null;
1524
            if (object instanceof FolderDTO)
1525
                    folder = (FolderDTO) object;
1526
            else
1527
                    file = (FileHeaderDTO) object;
1528

    
1529
            if (file != null)
1530
                        try {
1531
                                getService().deleteFile(user.getId(), file.getId());
1532
                } catch (InsufficientPermissionsException e) {
1533
                        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1534
                                return;
1535
                    } catch (ObjectNotFoundException e) {
1536
                            // Although we had already found the object, it was
1537
                            // probably deleted from another thread.
1538
                            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1539
                            return;
1540
                    } catch (RpcException e) {
1541
                            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1542
                            return;
1543
                    }
1544
                else if (folder != null)
1545
                        try {
1546
                            getService().deleteFolder(user.getId(), folder.getId());
1547
                } catch (InsufficientPermissionsException e) {
1548
                        resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1549
                        return;
1550
                    } catch (ObjectNotFoundException e) {
1551
                        resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
1552
                        return;
1553
                    } catch (RpcException e) {
1554
                        resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1555
                        return;
1556
                    }
1557
                resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1558
            return;
1559
    }
1560

    
1561
        /**
1562
     * Return an InputStream to a JSON representation of the contents
1563
     * of this directory.
1564
     *
1565
         * @param user the user that made the request
1566
     * @param folder the specified directory
1567
     * @param folderUrl the URL of the folder
1568
     * @return an input stream with the rendered contents
1569
         * @throws IOException if the response cannot be sent
1570
     * @throws ServletException
1571
         * @throws InsufficientPermissionsException if the user does not have
1572
         *                         the necessary privileges to read the directory
1573
     */
1574
    private InputStream renderJson(User user, FolderDTO folder, String folderUrl) throws IOException,
1575
                    ServletException, InsufficientPermissionsException {
1576
            JSONObject json = new JSONObject();
1577
            try {
1578
                        json.put("name", folder.getName()).
1579
                                        put("owner", folder.getOwner().getUsername()).
1580
                                        put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()).
1581
                                        put("creationDate", folder.getAuditInfo().getCreationDate().getTime()).
1582
                                        put("deleted", folder.isDeleted());
1583
                        if (folder.getParent() != null)
1584
                                json.put("parent", folder.getParent().getURI());
1585
                        if (folder.getAuditInfo().getModifiedBy() != null)
1586
                                json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()).
1587
                                                put("modificationDate", folder.getAuditInfo().getModificationDate().getTime());
1588
                    List<JSONObject> subfolders = new ArrayList<JSONObject>();
1589
                    for (FolderDTO f: folder.getSubfolders())
1590
                                if (!f.isDeleted()) {
1591
                                        JSONObject j = new JSONObject();
1592
                                        j.put("name", f.getName()).
1593
                                                put("uri", folderUrl + URLEncoder.encode(f.getName(), "UTF-8"));
1594
                                        subfolders.add(j);
1595
                                }
1596
                    json.put("folders", subfolders);
1597
                    List<JSONObject> files = new ArrayList<JSONObject>();
1598
                    List<FileHeaderDTO> fileHeaders = getService().getFiles(user.getId(), folder.getId(), false);
1599
                    for (FileHeaderDTO f: fileHeaders) {
1600
                            JSONObject j = new JSONObject();
1601
                                j.put("name", f.getName()).
1602
                                        put("owner", f.getOwner().getUsername()).
1603
                                        put("deleted", f.isDeleted()).
1604
                                        put("version", f.getVersion()).
1605
                                        put("size", f.getFileSize()).
1606
                                        put("creationDate", f.getAuditInfo().getCreationDate().getTime()).
1607
                                        put("uri", folderUrl + URLEncoder.encode(f.getName(), "UTF-8"));
1608
                                files.add(j);
1609
                    }
1610
                    json.put("files", files);
1611
                    Set<PermissionDTO> perms = getService().getFolderPermissions(user.getId(), folder.getId());
1612
                    json.put("permissions", renderJson(perms));
1613
                } catch (JSONException e) {
1614
                        throw new ServletException(e);
1615
                } catch (ObjectNotFoundException e) {
1616
                        throw new ServletException(e);
1617
                } catch (RpcException e) {
1618
                        throw new ServletException(e);
1619
                }
1620

    
1621
            // Prepare a writer to a buffered area
1622
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
1623
            OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
1624
            PrintWriter writer = new PrintWriter(osWriter);
1625

    
1626
            // Return an input stream to the underlying bytes
1627
            writer.write(json.toString());
1628
            writer.flush();
1629
            return new ByteArrayInputStream(stream.toByteArray());
1630
    }
1631

    
1632
        /**
1633
     * Return a String with a JSON representation of the metadata
1634
     * of the specified file. If an old file body is provided, then
1635
     * the metadata of that particular version will be returned.
1636
     *
1637
         * @param user the user that made the request
1638
     * @param file the specified file header
1639
     * @param oldBody the version number
1640
     * @return the JSON-encoded file
1641
     * @throws ServletException
1642
         * @throws InsufficientPermissionsException if the user does not have
1643
         *                         the necessary privileges to read the directory
1644
     */
1645
    private String renderJson(User user, FileHeaderDTO file, FileBodyDTO oldBody)
1646
                    throws ServletException, InsufficientPermissionsException {
1647
            JSONObject json = new JSONObject();
1648
            try {
1649
                    //need to encode file name in order to properly display it in gwt
1650
                        json.put("name", URLEncoder.encode(file.getName(),"UTF-8")).
1651
                                        put("owner", file.getOwner().getUsername()).
1652
                                        put("versioned", file.isVersioned()).
1653
                                        put("version", oldBody != null ? oldBody.getVersion() : file.getVersion()).
1654
                                        put("readForAll", file.isReadForAll()).
1655
                                        put("tags", file.getTags()).
1656
                                        put("folder", file.getFolder().getURI()).
1657
                                        put("deleted", file.isDeleted());
1658
                        if (oldBody != null)
1659
                                json.put("createdBy", oldBody.getAuditInfo().getCreatedBy().getUsername()).
1660
                                                put("creationDate", oldBody.getAuditInfo().getCreationDate().getTime()).
1661
                                                put("modifiedBy", oldBody.getAuditInfo().getModifiedBy().getUsername()).
1662
                                                put("modificationDate", oldBody.getAuditInfo().getModificationDate().getTime()).
1663
                                                put("size", oldBody.getFileSize());
1664
                        else
1665
                                json.put("createdBy", file.getAuditInfo().getCreatedBy().getUsername()).
1666
                                                put("creationDate", file.getAuditInfo().getCreationDate().getTime()).
1667
                                                put("modifiedBy", file.getAuditInfo().getModifiedBy().getUsername()).
1668
                                                put("modificationDate", file.getAuditInfo().getModificationDate().getTime()).
1669
                                                put("size", file.getFileSize());
1670
                    Set<PermissionDTO> perms = getService().getFilePermissions(user.getId(), file.getId());
1671
                    json.put("permissions", renderJson(perms));
1672
                } catch (JSONException e) {
1673
                        throw new ServletException(e);
1674
                } catch (ObjectNotFoundException e) {
1675
                        throw new ServletException(e);
1676
                } catch (RpcException e) {
1677
                        throw new ServletException(e);
1678
                } catch (UnsupportedEncodingException e) {
1679
                        throw new ServletException(e);
1680
                }
1681

    
1682
            return json.toString();
1683
    }
1684

    
1685
        /**
1686
         * Return a String with a JSON representation of the
1687
         * specified set of permissions.
1688
     *
1689
         * @param permissions the set of permissions
1690
         * @return the JSON-encoded object
1691
         * @throws JSONException
1692
         */
1693
        private JSONArray renderJson(Set<PermissionDTO> permissions) throws JSONException {
1694
                JSONArray perms = new JSONArray();
1695
                for (PermissionDTO p: permissions) {
1696
                        JSONObject permission = new JSONObject();
1697
                        permission.put("read", p.hasRead()).put("write", p.hasWrite()).put("modifyACL", p.hasModifyACL());
1698
                        if (p.getUser() != null)
1699
                                permission.put("user", p.getUser().getUsername());
1700
                        if (p.getGroup() != null)
1701
                                permission.put("group", p.getGroup().getName());
1702
                        perms.put(permission);
1703
                }
1704
                return perms;
1705
        }
1706

    
1707
        /**
1708
         * Retrieves the user who owns the destination namespace, for a
1709
         * copy or move request.
1710
         *
1711
         * @param req the HTTP request
1712
         * @return the owner of the namespace
1713
         */
1714
        protected User getDestinationOwner(HttpServletRequest req) {
1715
                return (User) req.getAttribute(DESTINATION_OWNER_ATTRIBUTE);
1716
        }
1717

    
1718
        /**
1719
         * A helper inner class for updating the progress status of a file upload.
1720
         *
1721
         * @author kman
1722
         */
1723
        public static class StatusProgressListener implements ProgressListener {
1724
                private int percentLogged = 0;
1725
                private long bytesTransferred = 0;
1726

    
1727
                private long fileSize = -100;
1728

    
1729
                private long tenKBRead = -1;
1730

    
1731
                private Long userId;
1732

    
1733
                private String filename;
1734

    
1735
                private ExternalAPI service;
1736

    
1737
                public StatusProgressListener(ExternalAPI aService) {
1738
                        service = aService;
1739
                }
1740

    
1741
                /**
1742
                 * Modify the userId.
1743
                 *
1744
                 * @param aUserId the userId to set
1745
                 */
1746
                public void setUserId(Long aUserId) {
1747
                        userId = aUserId;
1748
                }
1749

    
1750
                /**
1751
                 * Modify the filename.
1752
                 *
1753
                 * @param aFilename the filename to set
1754
                 */
1755
                public void setFilename(String aFilename) {
1756
                        filename = aFilename;
1757
                }
1758

    
1759
                public void update(long bytesRead, long contentLength, int items) {
1760
                        //monitoring per percent of bytes uploaded
1761
                        bytesTransferred = bytesRead;
1762
                        if (fileSize != contentLength)
1763
                                fileSize = contentLength;
1764
                        int percent = new Long(bytesTransferred * 100 / fileSize).intValue();
1765

    
1766
                        if (percent < 5 || percent % TRACK_PROGRESS_PERCENT == 0 )
1767
                                if (percent != percentLogged){
1768
                                        percentLogged = percent;
1769
                                        try {
1770
                                                if (userId != null && filename != null)
1771
                                                        service.createFileUploadProgress(userId, filename, bytesTransferred, fileSize);
1772
                                        } catch (ObjectNotFoundException e) {
1773
                                                // Swallow the exception since it is going to be caught
1774
                                                // by previously called methods
1775
                                        }
1776
                                }
1777
                }
1778
        }
1779
}