2 * Copyright 2008, 2009 Electronic Business Systems Ltd.
4 * This file is part of GSS.
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.
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.
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/>.
19 package gr.ebs.gss.server.rest;
21 import gr.ebs.gss.client.domain.FileBodyDTO;
22 import gr.ebs.gss.client.domain.FileHeaderDTO;
23 import gr.ebs.gss.client.domain.FolderDTO;
24 import gr.ebs.gss.client.domain.GroupDTO;
25 import gr.ebs.gss.client.domain.PermissionDTO;
26 import gr.ebs.gss.client.exceptions.DuplicateNameException;
27 import gr.ebs.gss.client.exceptions.GSSIOException;
28 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
29 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
30 import gr.ebs.gss.client.exceptions.QuotaExceededException;
31 import gr.ebs.gss.client.exceptions.RpcException;
32 import gr.ebs.gss.server.domain.FileUploadStatus;
33 import gr.ebs.gss.server.domain.User;
34 import gr.ebs.gss.server.ejb.ExternalAPI;
35 import gr.ebs.gss.server.webdav.Range;
37 import java.io.BufferedReader;
38 import java.io.ByteArrayInputStream;
39 import java.io.ByteArrayOutputStream;
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;
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;
57 import java.util.StringTokenizer;
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;
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;
81 * A class that handles operations on the 'files' namespace.
85 public class FilesHandler extends RequestHandler {
87 * The request parameter name for fetching a different version.
89 private static final String VERSION_PARAM = "version";
92 * The request attribute containing the owner of the destination URI
93 * in a copy or move request.
95 private static final String DESTINATION_OWNER_ATTRIBUTE = "destOwner";
97 private static final int TRACK_PROGRESS_PERCENT = 5;
100 * The form parameter name that contains the signature in a browser POST upload.
102 private static final String AUTHORIZATION_PARAMETER = "Authorization";
105 * The form parameter name that contains the date in a browser POST upload.
107 private static final String DATE_PARAMETER = "Date";
110 * The request parameter name for making an upload progress request.
112 private static final String PROGRESS_PARAMETER = "progress";
117 private static Log logger = LogFactory.getLog(FilesHandler.class);
120 * The servlet context provided by the call site.
122 private ServletContext context;
125 * @param servletContext
127 public FilesHandler(ServletContext servletContext) {
128 context = servletContext;
132 * Serve the specified resource, optionally including the data content.
134 * @param req The servlet request we are processing
135 * @param resp The servlet response we are creating
136 * @param content Should the content be included?
138 * @exception IOException if an input/output error occurs
139 * @exception ServletException if a servlet-specified error occurs
140 * @throws RpcException
141 * @throws InsufficientPermissionsException
142 * @throws ObjectNotFoundException
145 protected void serveResource(HttpServletRequest req, HttpServletResponse resp, boolean content)
146 throws IOException, ServletException {
147 boolean authDeferred = getAuthDeferred(req);
148 String path = getInnerPath(req, PATH_FILES);
151 path = URLDecoder.decode(path, "UTF-8");
152 String progress = req.getParameter(PROGRESS_PARAMETER);
154 if (logger.isDebugEnabled())
156 logger.debug("Serving resource '" + path + "' headers and data");
158 logger.debug("Serving resource '" + path + "' headers only");
160 User user = getUser(req);
161 User owner = getOwner(req);
162 if (user == null) user = owner;
163 boolean exists = true;
164 Object resource = null;
165 FileHeaderDTO file = null;
166 FolderDTO folder = null;
168 resource = getService().getResourceAtPath(owner.getId(), path, false);
169 } catch (ObjectNotFoundException e) {
171 } catch (RpcException e) {
172 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
178 // We do not want to leak information if the request
179 // was not authenticated.
180 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
183 // A request for upload progress.
184 if (progress != null && content) {
185 serveProgress(req, resp, progress, user, null);
189 resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
193 if (resource instanceof FolderDTO)
194 folder = (FolderDTO) resource;
196 file = (FileHeaderDTO) resource;
198 // Now it's time to perform the deferred authentication check.
199 // Since regular signature checking was already performed,
200 // we need to check the read-all flag or the signature-in-parameters.
202 if (file != null && !file.isReadForAll() && content) {
203 // Check for GET with the signature in the request parameters.
204 String auth = req.getParameter(AUTHORIZATION_PARAMETER);
205 String dateParam = req.getParameter(DATE_PARAMETER);
206 if (auth == null || dateParam == null) {
207 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
213 timestamp = DateUtil.parseDate(dateParam).getTime();
214 } catch (DateParseException e) {
215 resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
218 if (!isTimeValid(timestamp)) {
219 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
223 // Fetch the Authorization parameter and find the user specified in it.
224 String[] authParts = auth.split(" ");
225 if (authParts.length != 2) {
226 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
229 String username = authParts[0];
230 String signature = authParts[1];
233 user = getService().findUser(username);
234 } catch (RpcException e) {
235 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
239 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
242 req.setAttribute(USER_ATTRIBUTE, user);
244 // Validate the signature in the Authorization parameter.
245 String data = req.getMethod() + dateParam + URLEncoder.encode(req.getPathInfo(), "UTF-8");
246 if (!isSignatureValid(signature, user, data)) {
247 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
250 } else if (file != null && !file.isReadForAll() || file == null) {
251 // Check for a read-for-all file request.
252 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
256 // If the resource is not a collection, and the resource path
257 // ends with "/" or "\", return NOT FOUND.
259 if (path.endsWith("/") || path.endsWith("\\")) {
260 resp.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI());
264 // A request for upload progress.
265 if (progress != null && content) {
267 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
270 serveProgress(req, resp, progress, user, file);
274 // Fetch the version to retrieve, if specified.
275 String verStr = req.getParameter(VERSION_PARAM);
277 FileBodyDTO oldBody = null;
278 if (verStr != null && file != null)
280 version = Integer.valueOf(verStr);
281 } catch (NumberFormatException e) {
282 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, req.getRequestURI());
287 oldBody = getService().getFileVersion(user.getId(), file.getId(), version);
288 } catch (RpcException e) {
289 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
291 } catch (ObjectNotFoundException e) {
292 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
294 } catch (InsufficientPermissionsException e) {
295 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
299 // Check if the conditions specified in the optional If headers are
300 // satisfied. Doing this for folders would require recursive checking
301 // for all of their children, which in turn would defy the purpose of
304 // Checking If headers.
305 if (!checkIfHeaders(req, resp, file, oldBody))
308 // Find content type.
309 String contentType = null;
311 contentType = version>0 ? oldBody.getMimeType() : file.getMimeType();
312 if (contentType == null) {
313 contentType = context.getMimeType(file.getName());
314 file.setMimeType(contentType);
317 contentType = "application/json;charset=UTF-8";
319 ArrayList ranges = null;
320 long contentLength = -1L;
323 // Parse range specifier
324 ranges = parseRange(req, resp, file, oldBody);
326 resp.setHeader("ETag", getETag(file, oldBody));
327 // Last-Modified header
328 String lastModified = oldBody == null ?
329 getLastModifiedHttp(file.getAuditInfo()) :
330 getLastModifiedHttp(oldBody.getAuditInfo());
331 resp.setHeader("Last-Modified", lastModified);
332 // X-GSS-Metadata header
334 resp.setHeader("X-GSS-Metadata", renderJson(user, file, oldBody));
335 } catch (InsufficientPermissionsException e) {
336 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
339 // Get content length
340 contentLength = version>0 ? oldBody.getFileSize() : file.getFileSize();
341 // Special case for zero length files, which would cause a
342 // (silent) ISE when setting the output buffer size
343 if (contentLength == 0L)
347 ServletOutputStream ostream = null;
348 PrintWriter writer = null;
352 ostream = resp.getOutputStream();
353 } catch (IllegalStateException e) {
354 // If it fails, we try to get a Writer instead if we're
355 // trying to serve a text file
356 if ( contentType == null
357 || contentType.startsWith("text")
358 || contentType.endsWith("xml") )
359 writer = resp.getWriter();
365 || (ranges == null || ranges.isEmpty())
366 && req.getHeader("Range") == null
368 // Set the appropriate output headers
369 if (contentType != null) {
370 if (logger.isDebugEnabled())
371 logger.debug("contentType='" + contentType + "'");
372 resp.setContentType(contentType);
374 if (file != null && contentLength >= 0) {
375 if (logger.isDebugEnabled())
376 logger.debug("contentLength=" + contentLength);
377 if (contentLength < Integer.MAX_VALUE)
378 resp.setContentLength((int) contentLength);
380 // Set the content-length as String to be able to use a long
381 resp.setHeader("content-length", "" + contentLength);
384 InputStream renderResult = null;
387 // Serve the directory browser
388 String parentUrl = getContextPath(req, true);
390 renderResult = renderJson(user, folder, parentUrl);
391 } catch (InsufficientPermissionsException e) {
392 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
396 // Copy the input stream to our output stream (if requested)
399 resp.setBufferSize(output);
400 } catch (IllegalStateException e) {
405 copy(file, renderResult, ostream, req, oldBody);
407 copy(file, renderResult, writer, req, oldBody);
408 } catch (ObjectNotFoundException e) {
409 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
411 } catch (InsufficientPermissionsException e) {
412 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
414 } catch (RpcException e) {
415 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
420 if (ranges == null || ranges.isEmpty())
422 // Partial content response.
423 resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
425 if (ranges.size() == 1) {
426 Range range = (Range) ranges.get(0);
427 resp.addHeader("Content-Range", "bytes "
429 + "-" + range.end + "/"
431 long length = range.end - range.start + 1;
432 if (length < Integer.MAX_VALUE)
433 resp.setContentLength((int) length);
435 // Set the content-length as String to be able to use a long
436 resp.setHeader("content-length", "" + length);
438 if (contentType != null) {
439 if (logger.isDebugEnabled())
440 logger.debug("contentType='" + contentType + "'");
441 resp.setContentType(contentType);
446 resp.setBufferSize(output);
447 } catch (IllegalStateException e) {
452 copy(file, ostream, range, req, oldBody);
454 copy(file, writer, range, req, oldBody);
455 } catch (ObjectNotFoundException e) {
456 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
458 } catch (InsufficientPermissionsException e) {
459 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
461 } catch (RpcException e) {
462 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
467 resp.setContentType("multipart/byteranges; boundary=" + mimeSeparation);
470 resp.setBufferSize(output);
471 } catch (IllegalStateException e) {
476 copy(file, ostream, ranges.iterator(), contentType, req, oldBody);
478 copy(file, writer, ranges.iterator(), contentType, req, oldBody);
479 } catch (ObjectNotFoundException e) {
480 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
482 } catch (InsufficientPermissionsException e) {
483 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
485 } catch (RpcException e) {
486 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
495 * Sends a progress update on the amount of bytes received until now for
496 * a file that the current user is currently uploading.
498 * @param req the HTTP request
499 * @param resp the HTTP response
500 * @param parameter the value for the progress request parameter
501 * @param user the current user
502 * @param file the file being uploaded, or null if the request is about a new file
503 * @throws IOException if an I/O error occurs
505 private void serveProgress(HttpServletRequest req, HttpServletResponse resp,
506 String parameter, User user, FileHeaderDTO file) throws IOException {
507 String filename = file == null ? parameter : file.getName();
509 FileUploadStatus status = getService().getFileUploadStatus(user.getId(), filename);
510 if (status == null) {
511 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
514 JSONObject json = new JSONObject();
515 json.put("bytesUploaded", status.getBytesUploaded()).
516 put("bytesTotal", status.getFileSize());
517 sendJson(req, resp, json.toString());
519 } catch (RpcException e) {
520 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
522 } catch (JSONException e) {
523 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
529 * Server a POST request to create/modify a file or folder.
531 * @param req the HTTP request
532 * @param resp the HTTP response
533 * @exception IOException if an input/output error occurs
535 void postResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
536 boolean authDeferred = getAuthDeferred(req);
537 if (!authDeferred && req.getParameterMap().size() > 1) {
538 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
541 String path = getInnerPath(req, PATH_FILES);
542 path = path.endsWith("/")? path: path + '/';
543 path = URLDecoder.decode(path, "UTF-8");
544 // We only defer authenticating multipart POST requests.
546 if (!ServletFileUpload.isMultipartContent(req)) {
547 resp.sendError(HttpServletResponse.SC_FORBIDDEN);
550 handleMultipart(req, resp, path);
554 String newName = req.getParameter(NEW_FOLDER_PARAMETER);
555 boolean hasUpdateParam = req.getParameterMap().containsKey(RESOURCE_UPDATE_PARAMETER);
556 boolean hasTrashParam = req.getParameterMap().containsKey(RESOURCE_TRASH_PARAMETER);
557 boolean hasRestoreParam = req.getParameterMap().containsKey(RESOURCE_RESTORE_PARAMETER);
558 String copyTo = req.getParameter(RESOURCE_COPY_PARAMETER);
559 String moveTo = req.getParameter(RESOURCE_MOVE_PARAMETER);
562 createFolder(req, resp, path, newName);
563 else if (hasUpdateParam)
564 updateResource(req, resp, path);
565 else if (hasTrashParam)
566 trashResource(req, resp, path);
567 else if (hasRestoreParam)
568 restoreResource(req, resp, path);
569 else if (copyTo != null)
570 copyResource(req, resp, path, copyTo);
571 else if (moveTo != null)
572 moveResource(req, resp, path, moveTo);
574 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
578 * A method for handling multipart POST requests for uploading
579 * files from browser-based JavaScript clients.
581 * @param request the HTTP request
582 * @param response the HTTP response
583 * @param path the resource path
584 * @throws IOException in case an error occurs writing to the
587 private void handleMultipart(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
588 if (logger.isDebugEnabled())
589 logger.debug("Multipart POST for resource: " + path);
591 User owner = getOwner(request);
592 boolean exists = true;
593 Object resource = null;
594 FileHeaderDTO file = null;
596 resource = getService().getResourceAtPath(owner.getId(), path, false);
597 } catch (ObjectNotFoundException e) {
599 } catch (RpcException e) {
600 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
605 if (resource instanceof FileHeaderDTO)
606 file = (FileHeaderDTO) resource;
608 response.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
612 FolderDTO folder = null;
614 String parentPath = null;
616 parentPath = getParentPath(path);
617 parent = getService().getResourceAtPath(owner.getId(), parentPath, true);
618 } catch (ObjectNotFoundException e) {
619 response.sendError(HttpServletResponse.SC_NOT_FOUND, parentPath);
621 } catch (RpcException e) {
622 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
625 if (!(parent instanceof FolderDTO)) {
626 response.sendError(HttpServletResponse.SC_CONFLICT);
629 folder = (FolderDTO) parent;
630 String fileName = getLastElement(path);
632 FileItemIterator iter;
633 File uploadedFile = null;
635 // Create a new file upload handler.
636 ServletFileUpload upload = new ServletFileUpload();
637 StatusProgressListener progressListener = new StatusProgressListener(getService());
638 upload.setProgressListener(progressListener);
639 iter = upload.getItemIterator(request);
640 String dateParam = null;
642 while (iter.hasNext()) {
643 FileItemStream item = iter.next();
644 String name = item.getFieldName();
645 InputStream stream = item.openStream();
646 if (item.isFormField()) {
647 final String value = Streams.asString(stream);
648 if (name.equals(DATE_PARAMETER))
650 else if (name.equals(AUTHORIZATION_PARAMETER))
653 if (logger.isDebugEnabled())
654 logger.debug(name + ":" + value);
656 // Fetch the timestamp used to guard against replay attacks.
657 if (dateParam == null) {
658 response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Date parameter");
664 timestamp = DateUtil.parseDate(dateParam).getTime();
665 } catch (DateParseException e) {
666 response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
669 if (!isTimeValid(timestamp)) {
670 response.sendError(HttpServletResponse.SC_FORBIDDEN);
674 // Fetch the Authorization parameter and find the user specified in it.
676 response.sendError(HttpServletResponse.SC_FORBIDDEN, "No Authorization parameter");
679 String[] authParts = auth.split(" ");
680 if (authParts.length != 2) {
681 response.sendError(HttpServletResponse.SC_FORBIDDEN);
684 String username = authParts[0];
685 String signature = authParts[1];
688 user = getService().findUser(username);
689 } catch (RpcException e) {
690 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
694 response.sendError(HttpServletResponse.SC_FORBIDDEN);
697 request.setAttribute(USER_ATTRIBUTE, user);
699 // Validate the signature in the Authorization parameter.
700 String data = request.getMethod() + dateParam + URLEncoder.encode(request.getPathInfo(), "UTF-8");
701 if (!isSignatureValid(signature, user, data)) {
702 response.sendError(HttpServletResponse.SC_FORBIDDEN);
706 progressListener.setUserId(user.getId());
707 progressListener.setFilename(fileName);
708 String contentType = item.getContentType();
711 uploadedFile = getService().uploadFile(stream, user.getId());
712 } catch (IOException ex) {
713 throw new GSSIOException(ex, false);
716 getService().createFile(user.getId(), folder.getId(), fileName, contentType, uploadedFile);
718 getService().updateFileContents(user.getId(), file.getId(), contentType, uploadedFile);
719 getService().removeFileUploadProgress(user.getId(), file.getName());
722 } catch (FileUploadException e) {
723 String error = "Error while uploading file";
724 logger.error(error, e);
725 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
726 } catch (GSSIOException e) {
727 if (uploadedFile != null && uploadedFile.exists())
728 uploadedFile.delete();
729 String error = "Error while uploading file";
731 logger.error(error, e);
733 logger.debug(error, e);
734 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
735 } catch (DuplicateNameException e) {
736 if (uploadedFile != null && uploadedFile.exists())
737 uploadedFile.delete();
738 String error = "The specified file name already exists in this folder";
739 logger.error(error, e);
740 response.sendError(HttpServletResponse.SC_CONFLICT, error);
742 } catch (InsufficientPermissionsException e) {
743 if (uploadedFile != null && uploadedFile.exists())
744 uploadedFile.delete();
745 String error = "You don't have the necessary permissions";
746 logger.error(error, e);
747 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, error);
749 } catch (QuotaExceededException e) {
750 if (uploadedFile != null && uploadedFile.exists())
751 uploadedFile.delete();
752 String error = "Not enough free space available";
753 if (logger.isDebugEnabled())
754 logger.debug(error, e);
755 response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, error);
757 } catch (ObjectNotFoundException e) {
758 if (uploadedFile != null && uploadedFile.exists())
759 uploadedFile.delete();
760 String error = "A specified object was not found";
761 logger.error(error, e);
762 response.sendError(HttpServletResponse.SC_NOT_FOUND, error);
763 } catch (RpcException e) {
764 if (uploadedFile != null && uploadedFile.exists())
765 uploadedFile.delete();
766 String error = "An error occurred while communicating with the service";
767 logger.error(error, e);
768 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
773 * Move the resource in the specified path to the specified destination.
775 * @param req the HTTP request
776 * @param resp the HTTP response
777 * @param path the path of the resource
778 * @param moveTo the destination of the move procedure
779 * @throws IOException if an input/output error occurs
781 private void moveResource(HttpServletRequest req, HttpServletResponse resp, String path, String moveTo) throws IOException {
782 User user = getUser(req);
783 User owner = getOwner(req);
784 Object resource = null;
786 resource = getService().getResourceAtPath(owner.getId(), path, true);
787 } catch (ObjectNotFoundException e) {
788 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
790 } catch (RpcException e) {
791 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
795 String destination = null;
796 User destOwner = null;
797 boolean exists = true;
799 destination = getDestinationPath(req, encodePath(moveTo));
800 destination = URLDecoder.decode(destination, "UTF-8");
801 destOwner = getDestinationOwner(req);
802 getService().getResourceAtPath(destOwner.getId(), destination, true);
803 } catch (ObjectNotFoundException e) {
805 } catch (URISyntaxException e) {
806 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
808 } catch (RpcException e) {
809 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
813 resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
818 if (resource instanceof FolderDTO) {
819 FolderDTO folder = (FolderDTO) resource;
820 getService().moveFolderToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
822 FileHeaderDTO file = (FileHeaderDTO) resource;
823 getService().moveFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
825 } catch (InsufficientPermissionsException e) {
826 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
827 } catch (ObjectNotFoundException e) {
828 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
829 } catch (RpcException e) {
830 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
831 } catch (DuplicateNameException e) {
832 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
833 } catch (GSSIOException e) {
834 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
835 } catch (QuotaExceededException e) {
836 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
841 * Copy the resource in the specified path to the specified destination.
843 * @param req the HTTP request
844 * @param resp the HTTP response
845 * @param path the path of the resource
846 * @param copyTo the destination of the copy procedure
847 * @throws IOException if an input/output error occurs
849 private void copyResource(HttpServletRequest req, HttpServletResponse resp, String path, String copyTo) throws IOException {
850 User user = getUser(req);
851 User owner = getOwner(req);
852 Object resource = null;
854 resource = getService().getResourceAtPath(owner.getId(), path, true);
855 } catch (ObjectNotFoundException e) {
856 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
858 } catch (RpcException e) {
859 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
863 String destination = null;
864 User destOwner = null;
865 boolean exists = true;
867 destination = getDestinationPath(req, encodePath(copyTo));
868 destination = URLDecoder.decode(destination, "UTF-8");
869 destOwner = getDestinationOwner(req);
870 getService().getResourceAtPath(destOwner.getId(), destination, true);
871 } catch (ObjectNotFoundException e) {
873 } catch (URISyntaxException e) {
874 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
876 } catch (RpcException e) {
877 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
881 resp.sendError(HttpServletResponse.SC_CONFLICT, destination + " already exists");
886 if (resource instanceof FolderDTO) {
887 FolderDTO folder = (FolderDTO) resource;
888 getService().copyFolderStructureToPath(user.getId(), destOwner.getId(), folder.getId(), destination);
890 FileHeaderDTO file = (FileHeaderDTO) resource;
891 getService().copyFileToPath(user.getId(), destOwner.getId(), file.getId(), destination);
893 } catch (InsufficientPermissionsException e) {
894 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
895 } catch (ObjectNotFoundException e) {
896 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
897 } catch (RpcException e) {
898 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, destination);
899 } catch (DuplicateNameException e) {
900 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
901 } catch (GSSIOException e) {
902 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
903 } catch (QuotaExceededException e) {
904 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
908 private String encodePath(String path) throws UnsupportedEncodingException{
909 StringTokenizer str = new StringTokenizer(path, "/:", true);
910 String result = new String();
911 while(str.hasMoreTokens()){
912 String token = str.nextToken();
913 if(!token.equals("/") && !token.equals(":"))
914 token = URLEncoder.encode(token,"UTF-8");
915 result = result + token;
920 * A helper method that extracts the relative resource path,
921 * after removing the 'files' namespace.
923 * @param req the HTTP request
924 * @param path the specified path
925 * @return the path relative to the root folder
926 * @throws URISyntaxException
927 * @throws RpcException in case an error occurs while communicating
930 private String getDestinationPath(HttpServletRequest req, String path) throws URISyntaxException, RpcException {
931 URI uri = new URI(path);
932 String dest = uri.getPath();
933 // Remove the context path from the destination URI.
934 String contextPath = req.getContextPath();
935 if (!dest.startsWith(contextPath))
936 throw new URISyntaxException(dest, "Destination path does not start with " + contextPath);
937 dest = dest.substring(contextPath.length());
938 // Remove the servlet path from the destination URI.
939 String servletPath = req.getServletPath();
940 if (!dest.startsWith(servletPath))
941 throw new URISyntaxException(dest, "Destination path does not start with " + servletPath);
942 dest = dest.substring(servletPath.length());
943 // Strip the username part
944 if (dest.length() < 2)
945 throw new URISyntaxException(dest, "No username in the destination URI");
946 int slash = dest.substring(1).indexOf('/');
948 throw new URISyntaxException(dest, "No username in the destination URI");
949 String owner = dest.substring(1, slash + 1);
951 o = getService().findUser(owner);
953 throw new URISyntaxException(dest, "User " + owner + " not found");
955 req.setAttribute(DESTINATION_OWNER_ATTRIBUTE, o);
956 dest = dest.substring(slash + 1);
958 // Chop the resource namespace part
959 dest = dest.substring(RequestHandler.PATH_FILES.length());
961 dest = dest.endsWith("/")? dest: dest + '/';
966 * Move the resource in the specified path to the trash bin.
968 * @param req the HTTP request
969 * @param resp the HTTP response
970 * @param path the path of the resource
971 * @throws IOException if an input/output error occurs
973 private void trashResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
974 User user = getUser(req);
975 User owner = getOwner(req);
976 Object resource = null;
978 resource = getService().getResourceAtPath(owner.getId(), path, true);
979 } catch (ObjectNotFoundException e) {
980 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
982 } catch (RpcException e) {
983 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
988 if (resource instanceof FolderDTO) {
989 FolderDTO folder = (FolderDTO) resource;
990 getService().moveFolderToTrash(user.getId(), folder.getId());
992 FileHeaderDTO file = (FileHeaderDTO) resource;
993 getService().moveFileToTrash(user.getId(), file.getId());
995 } catch (InsufficientPermissionsException e) {
996 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
997 } catch (ObjectNotFoundException e) {
998 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
999 } catch (RpcException e) {
1000 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1005 * Restore the resource in the specified path from the trash bin.
1007 * @param req the HTTP request
1008 * @param resp the HTTP response
1009 * @param path the path of the resource
1010 * @throws IOException if an input/output error occurs
1012 private void restoreResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1013 User user = getUser(req);
1014 User owner = getOwner(req);
1015 Object resource = null;
1017 resource = getService().getResourceAtPath(owner.getId(), path, false);
1018 } catch (ObjectNotFoundException e) {
1019 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1021 } catch (RpcException e) {
1022 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1027 if (resource instanceof FolderDTO) {
1028 FolderDTO folder = (FolderDTO) resource;
1029 getService().removeFolderFromTrash(user.getId(), folder.getId());
1031 FileHeaderDTO file = (FileHeaderDTO) resource;
1032 getService().removeFileFromTrash(user.getId(), file.getId());
1034 } catch (InsufficientPermissionsException e) {
1035 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1036 } catch (ObjectNotFoundException e) {
1037 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1038 } catch (RpcException e) {
1039 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1044 * Update the resource in the specified path.
1046 * @param req the HTTP request
1047 * @param resp the HTTP response
1048 * @param path the path of the resource
1049 * @throws IOException if an input/output error occurs
1051 private void updateResource(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
1052 User user = getUser(req);
1053 User owner = getOwner(req);
1054 Object resource = null;
1056 resource = getService().getResourceAtPath(owner.getId(), path, true);
1057 } catch (ObjectNotFoundException e) {
1058 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
1060 } catch (RpcException e) {
1061 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1064 //use utf-8 encoding for reading request
1065 BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8"));
1066 StringBuffer input = new StringBuffer();
1068 JSONObject json = null;
1069 while ((line = reader.readLine()) != null)
1073 json = new JSONObject(input.toString());
1074 if (logger.isDebugEnabled())
1075 logger.debug("JSON update: " + json);
1076 if (resource instanceof FolderDTO) {
1077 FolderDTO folder = (FolderDTO) resource;
1078 String name = json.optString("name");
1079 if (!name.isEmpty()){
1080 getService().modifyFolder(user.getId(), folder.getId(), name);
1081 FolderDTO folderUpdated = getService().getFolder(user.getId(), folder.getId());
1082 String parentUrl =URLDecoder.decode(getContextPath(req, true),"UTF-8");
1083 String fpath = URLDecoder.decode(req.getPathInfo(), "UTF-8");
1084 parentUrl = parentUrl.replaceAll(fpath, "");
1085 if(!parentUrl.endsWith("/"))
1086 parentUrl = parentUrl+"/";
1087 parentUrl = parentUrl+folderUpdated.getOwner().getUsername()+PATH_FILES+folderUpdated.getPath();
1088 resp.getWriter().println(parentUrl);
1091 JSONArray permissions = json.optJSONArray("permissions");
1092 if (permissions != null) {
1093 Set<PermissionDTO> perms = parsePermissions(user, permissions);
1094 getService().setFolderPermissions(user.getId(), folder.getId(), perms);
1097 FileHeaderDTO file = (FileHeaderDTO) resource;
1099 if (json.opt("name") != null)
1100 name = json.optString("name");
1101 JSONArray tagset = json.optJSONArray("tags");
1103 StringBuffer t = new StringBuffer();
1104 if (tagset != null) {
1105 for (int i = 0; i < tagset.length(); i++)
1106 t.append(tagset.getString(i) + ',');
1107 tags = t.toString();
1109 if (name != null || tags != null)
1110 getService().updateFile(user.getId(), file.getId(), name, tags);
1112 JSONArray permissions = json.optJSONArray("permissions");
1113 Set<PermissionDTO> perms = null;
1114 if (permissions != null)
1115 perms = parsePermissions(user, permissions);
1116 Boolean readForAll = null;
1117 if (json.opt("readForAll") != null)
1118 readForAll = json.optBoolean("readForAll");
1119 if (perms != null || readForAll != null)
1120 getService().setFilePermissions(user.getId(), file.getId(), readForAll, perms);
1122 if (json.opt("versioned") != null) {
1123 boolean versioned = json.getBoolean("versioned");
1124 getService().toggleFileVersioning(user.getId(), file.getId(), versioned);
1127 } catch (JSONException e) {
1128 resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
1129 } catch (InsufficientPermissionsException e) {
1130 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1131 } catch (ObjectNotFoundException e) {
1132 resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
1133 } catch (DuplicateNameException e) {
1134 resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
1135 } catch (RpcException e) {
1136 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1141 * Helper method to convert a JSON array of permissions into a set of
1142 * PermissionDTO objects.
1144 * @param user the current user
1145 * @param permissions the JSON array to parse
1146 * @return the parsed set of permissions
1147 * @throws JSONException if there was an error parsing the JSON object
1148 * @throws RpcException if there was an error communicating with the EJB
1149 * @throws ObjectNotFoundException if the user could not be found
1151 private Set<PermissionDTO> parsePermissions(User user, JSONArray permissions)
1152 throws JSONException, RpcException, ObjectNotFoundException {
1153 if (permissions == null)
1155 Set<PermissionDTO> perms = new HashSet<PermissionDTO>();
1156 for (int i = 0; i < permissions.length(); i++) {
1157 JSONObject j = permissions.getJSONObject(i);
1158 PermissionDTO perm = new PermissionDTO();
1159 perm.setModifyACL(j.optBoolean("modifyACL"));
1160 perm.setRead(j.optBoolean("read"));
1161 perm.setWrite(j.optBoolean("write"));
1162 String permUser = j.optString("user");
1163 if (!permUser.isEmpty()) {
1164 User u = getService().findUser(permUser);
1166 throw new ObjectNotFoundException("User " + permUser + " not found");
1167 perm.setUser(u.getDTO());
1169 String permGroup = j.optString("group");
1170 if (!permGroup.isEmpty()) {
1171 GroupDTO g = getService().getGroup(user.getId(), permGroup);
1174 if (permUser.isEmpty() && permGroup.isEmpty() ||
1175 permUser.isEmpty() && permGroup.isEmpty())
1176 throw new JSONException("A permission must correspond to either a user or a group");
1183 * Creates a new folder with the specified name under the folder in the provided path.
1185 * @param req the HTTP request
1186 * @param resp the HTTP response
1187 * @param path the parent folder path
1188 * @param folderName the name of the new folder
1189 * @throws IOException if an input/output error occurs
1191 private void createFolder(HttpServletRequest req, HttpServletResponse resp, String path, String folderName) throws IOException {
1192 if (logger.isDebugEnabled())
1193 logger.debug("Creating folder " + folderName + " in '" + path);
1195 User user = getUser(req);
1196 User owner = getOwner(req);
1197 boolean exists = true;
1199 getService().getResourceAtPath(owner.getId(), path + folderName, false);
1200 } catch (ObjectNotFoundException e) {
1202 } catch (RpcException e) {
1203 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1208 resp.addHeader("Allow", METHOD_GET + ", " + METHOD_DELETE +
1209 ", " + METHOD_HEAD);
1210 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1216 parent = getService().getResourceAtPath(owner.getId(), path, true);
1217 } catch (ObjectNotFoundException e) {
1218 resp.sendError(HttpServletResponse.SC_CONFLICT);
1220 } catch (RpcException e) {
1221 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1225 if (parent instanceof FolderDTO) {
1226 FolderDTO folder = (FolderDTO) parent;
1227 getService().createFolder(user.getId(), folder.getId(), folderName);
1228 String newResource = getContextPath(req, true) + folderName;
1229 resp.setHeader("Location", newResource);
1230 resp.setContentType("text/plain");
1231 PrintWriter out = resp.getWriter();
1232 out.println(newResource);
1234 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1237 } catch (DuplicateNameException e) {
1238 resp.sendError(HttpServletResponse.SC_CONFLICT);
1240 } catch (InsufficientPermissionsException e) {
1241 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1243 } catch (ObjectNotFoundException e) {
1244 resp.sendError(HttpServletResponse.SC_CONFLICT);
1246 } catch (RpcException e) {
1247 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path + folderName);
1250 resp.setStatus(HttpServletResponse.SC_CREATED);
1256 * @throws IOException
1257 * @throws FileNotFoundException
1259 void putResource(HttpServletRequest req, HttpServletResponse resp) throws IOException, FileNotFoundException {
1260 String path = getInnerPath(req, PATH_FILES);
1261 if (logger.isDebugEnabled())
1262 logger.debug("Updating resource: " + path);
1264 User user = getUser(req);
1265 User owner = getOwner(req);
1266 boolean exists = true;
1267 Object resource = null;
1268 FileHeaderDTO file = null;
1270 resource = getService().getResourceAtPath(owner.getId(), path, false);
1271 } catch (ObjectNotFoundException e) {
1273 } catch (RpcException e) {
1274 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1279 if (resource instanceof FileHeaderDTO)
1280 file = (FileHeaderDTO) resource;
1282 resp.sendError(HttpServletResponse.SC_CONFLICT, path + " is a folder");
1285 boolean result = true;
1287 // Temporary content file used to support partial PUT.
1288 File contentFile = null;
1290 Range range = parseContentRange(req, resp);
1292 InputStream resourceInputStream = null;
1294 // Append data specified in ranges to existing content for this
1295 // resource - create a temporary file on the local filesystem to
1296 // perform this operation.
1297 // Assume just one range is specified for now
1298 if (range != null) {
1300 contentFile = executePartialPut(req, range, path);
1301 } catch (RpcException e) {
1302 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1304 } catch (ObjectNotFoundException e) {
1305 resp.sendError(HttpServletResponse.SC_CONFLICT);
1307 } catch (InsufficientPermissionsException e) {
1308 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1311 resourceInputStream = new FileInputStream(contentFile);
1313 resourceInputStream = req.getInputStream();
1316 FolderDTO folder = null;
1317 Object parent = getService().getResourceAtPath(owner.getId(), getParentPath(path), true);
1318 if (!(parent instanceof FolderDTO)) {
1319 resp.sendError(HttpServletResponse.SC_CONFLICT);
1322 folder = (FolderDTO) parent;
1323 String name = getLastElement(path);
1324 String mimeType = context.getMimeType(name);
1325 // FIXME: Add attributes
1327 getService().updateFileContents(user.getId(), file.getId(), mimeType, resourceInputStream);
1329 getService().createFile(user.getId(), folder.getId(), name, mimeType, resourceInputStream);
1330 getService().removeFileUploadProgress(user.getId(), file.getName());
1331 } catch(ObjectNotFoundException e) {
1333 } catch (RpcException e) {
1334 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1336 } catch (IOException e) {
1337 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1339 } catch (GSSIOException e) {
1340 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, path);
1342 } catch (DuplicateNameException e) {
1343 resp.sendError(HttpServletResponse.SC_CONFLICT);
1345 } catch (InsufficientPermissionsException e) {
1346 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1348 } catch (QuotaExceededException e) {
1349 resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, e.getMessage());
1355 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1357 resp.setStatus(HttpServletResponse.SC_CREATED);
1359 resp.sendError(HttpServletResponse.SC_CONFLICT);
1363 * Delete a resource.
1365 * @param req The servlet request we are processing
1366 * @param resp The servlet response we are processing
1367 * @throws IOException if the response cannot be sent
1369 void deleteResource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
1370 String path = getInnerPath(req, PATH_FILES);
1371 if (logger.isDebugEnabled())
1372 logger.debug("Deleting resource '" + path);
1373 path = URLDecoder.decode(path, "UTF-8");
1374 User user = getUser(req);
1375 User owner = getOwner(req);
1376 boolean exists = true;
1377 Object object = null;
1379 object = getService().getResourceAtPath(owner.getId(), path, false);
1380 } catch (ObjectNotFoundException e) {
1382 } catch (RpcException e) {
1383 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1388 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1392 FolderDTO folder = null;
1393 FileHeaderDTO file = null;
1394 if (object instanceof FolderDTO)
1395 folder = (FolderDTO) object;
1397 file = (FileHeaderDTO) object;
1401 getService().deleteFile(user.getId(), file.getId());
1402 } catch (InsufficientPermissionsException e) {
1403 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1405 } catch (ObjectNotFoundException e) {
1406 // Although we had already found the object, it was
1407 // probably deleted from another thread.
1408 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
1410 } catch (RpcException e) {
1411 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1414 else if (folder != null)
1416 getService().deleteFolder(user.getId(), folder.getId());
1417 } catch (InsufficientPermissionsException e) {
1418 resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
1420 } catch (ObjectNotFoundException e) {
1421 resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
1423 } catch (RpcException e) {
1424 resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
1427 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
1432 * Return an InputStream to a JSON representation of the contents
1433 * of this directory.
1435 * @param user the user that made the request
1436 * @param folder the specified directory
1437 * @param folderUrl the URL of the folder
1438 * @return an input stream with the rendered contents
1439 * @throws IOException if the response cannot be sent
1440 * @throws ServletException
1441 * @throws InsufficientPermissionsException if the user does not have
1442 * the necessary privileges to read the directory
1444 private InputStream renderJson(User user, FolderDTO folder, String folderUrl) throws IOException,
1445 ServletException, InsufficientPermissionsException {
1446 JSONObject json = new JSONObject();
1448 json.put("name", folder.getName()).
1449 put("owner", folder.getOwner().getUsername()).
1450 put("createdBy", folder.getAuditInfo().getCreatedBy().getUsername()).
1451 put("creationDate", folder.getAuditInfo().getCreationDate().getTime()).
1452 put("deleted", folder.isDeleted());
1453 if (folder.getParent() != null)
1454 json.put("parent", folder.getParent().getURI());
1455 if (folder.getAuditInfo().getModifiedBy() != null)
1456 json.put("modifiedBy", folder.getAuditInfo().getModifiedBy().getUsername()).
1457 put("modificationDate", folder.getAuditInfo().getModificationDate().getTime());
1458 List<JSONObject> subfolders = new ArrayList<JSONObject>();
1459 for (FolderDTO f: folder.getSubfolders())
1460 if (!f.isDeleted()) {
1461 JSONObject j = new JSONObject();
1462 j.put("name", f.getName()).
1463 put("uri", folderUrl + URLEncoder.encode(f.getName(), "UTF-8"));
1466 json.put("folders", subfolders);
1467 List<JSONObject> files = new ArrayList<JSONObject>();
1468 List<FileHeaderDTO> fileHeaders = getService().getFiles(user.getId(), folder.getId());
1469 for (FileHeaderDTO f: fileHeaders) {
1470 JSONObject j = new JSONObject();
1471 j.put("name", f.getName()).
1472 put("owner", f.getOwner().getUsername()).
1473 put("deleted", f.isDeleted()).
1474 put("version", f.getVersion()).
1475 put("size", f.getFileSize()).
1476 put("creationDate", f.getAuditInfo().getCreationDate().getTime()).
1477 put("uri", folderUrl + URLEncoder.encode(f.getName(), "UTF-8"));
1480 json.put("files", files);
1481 Set<PermissionDTO> perms = getService().getFolderPermissions(user.getId(), folder.getId());
1482 json.put("permissions", renderJson(perms));
1483 } catch (JSONException e) {
1484 throw new ServletException(e);
1485 } catch (ObjectNotFoundException e) {
1486 throw new ServletException(e);
1487 } catch (RpcException e) {
1488 throw new ServletException(e);
1491 // Prepare a writer to a buffered area
1492 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1493 OutputStreamWriter osWriter = new OutputStreamWriter(stream, "UTF8");
1494 PrintWriter writer = new PrintWriter(osWriter);
1496 // Return an input stream to the underlying bytes
1497 writer.write(json.toString());
1499 return new ByteArrayInputStream(stream.toByteArray());
1503 * Return a String with a JSON representation of the metadata
1504 * of the specified file. If an old file body is provided, then
1505 * the metadata of that particular version will be returned.
1507 * @param user the user that made the request
1508 * @param file the specified file header
1509 * @param oldBody the version number
1510 * @return the JSON-encoded file
1511 * @throws ServletException
1512 * @throws InsufficientPermissionsException if the user does not have
1513 * the necessary privileges to read the directory
1515 private String renderJson(User user, FileHeaderDTO file, FileBodyDTO oldBody)
1516 throws ServletException, InsufficientPermissionsException {
1517 JSONObject json = new JSONObject();
1519 //need to encode file name in order to properly display it in gwt
1520 json.put("name", URLEncoder.encode(file.getName(),"UTF-8")).
1521 put("owner", file.getOwner().getUsername()).
1522 put("versioned", file.isVersioned()).
1523 put("version", oldBody != null ? oldBody.getVersion() : file.getVersion()).
1524 put("readForAll", file.isReadForAll()).
1525 put("tags", file.getTags()).
1526 put("folder", file.getFolder().getURI()).
1527 put("deleted", file.isDeleted());
1528 if (oldBody != null)
1529 json.put("createdBy", oldBody.getAuditInfo().getCreatedBy().getUsername()).
1530 put("creationDate", oldBody.getAuditInfo().getCreationDate().getTime()).
1531 put("modifiedBy", oldBody.getAuditInfo().getModifiedBy().getUsername()).
1532 put("modificationDate", oldBody.getAuditInfo().getModificationDate().getTime());
1534 json.put("createdBy", file.getAuditInfo().getCreatedBy().getUsername()).
1535 put("creationDate", file.getAuditInfo().getCreationDate().getTime()).
1536 put("modifiedBy", file.getAuditInfo().getModifiedBy().getUsername()).
1537 put("modificationDate", file.getAuditInfo().getModificationDate().getTime());
1538 Set<PermissionDTO> perms = getService().getFilePermissions(user.getId(), file.getId());
1539 json.put("permissions", renderJson(perms));
1540 } catch (JSONException e) {
1541 throw new ServletException(e);
1542 } catch (ObjectNotFoundException e) {
1543 throw new ServletException(e);
1544 } catch (RpcException e) {
1545 throw new ServletException(e);
1546 } catch (UnsupportedEncodingException e) {
1547 throw new ServletException(e);
1550 return json.toString();
1554 * Return a String with a JSON representation of the
1555 * specified set of permissions.
1557 * @param permissions the set of permissions
1558 * @return the JSON-encoded object
1559 * @throws JSONException
1561 private JSONArray renderJson(Set<PermissionDTO> permissions) throws JSONException {
1562 JSONArray perms = new JSONArray();
1563 for (PermissionDTO p: permissions) {
1564 JSONObject permission = new JSONObject();
1565 permission.put("read", p.hasRead()).put("write", p.hasWrite()).put("modifyACL", p.hasModifyACL());
1566 if (p.getUser() != null)
1567 permission.put("user", p.getUser().getUsername());
1568 if (p.getGroup() != null)
1569 permission.put("group", p.getGroup().getName());
1570 perms.put(permission);
1576 * Retrieves the user who owns the destination namespace, for a
1577 * copy or move request.
1579 * @param req the HTTP request
1580 * @return the owner of the namespace
1582 protected User getDestinationOwner(HttpServletRequest req) {
1583 return (User) req.getAttribute(DESTINATION_OWNER_ATTRIBUTE);
1587 * A helper inner class for updating the progress status of a file upload.
1591 public static class StatusProgressListener implements ProgressListener {
1592 private int percentLogged = 0;
1593 private long bytesTransferred = 0;
1595 private long fileSize = -100;
1597 private long tenKBRead = -1;
1599 private Long userId;
1601 private String filename;
1603 private ExternalAPI service;
1605 public StatusProgressListener(ExternalAPI aService) {
1610 * Modify the userId.
1612 * @param aUserId the userId to set
1614 public void setUserId(Long aUserId) {
1619 * Modify the filename.
1621 * @param aFilename the filename to set
1623 public void setFilename(String aFilename) {
1624 filename = aFilename;
1627 public void update(long bytesRead, long contentLength, int items) {
1628 //monitoring per percent of bytes uploaded
1629 bytesTransferred = bytesRead;
1630 if (fileSize != contentLength)
1631 fileSize = contentLength;
1632 int percent = new Long(bytesTransferred * 100 / fileSize).intValue();
1634 if (percent < 5 || percent % TRACK_PROGRESS_PERCENT == 0 )
1635 if (percent != percentLogged){
1636 percentLogged = percent;
1638 if (userId != null && filename != null)
1639 service.createFileUploadProgress(userId, filename, bytesTransferred, fileSize);
1640 } catch (ObjectNotFoundException e) {
1641 // Swallow the exception since it is going to be caught
1642 // by previously called methods